Merge "Retry pullfinish with empty payload if call fails" into rvc-dev
diff --git a/adb/Android.bp b/adb/Android.bp
index cb5c1df..f8e5b38 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -567,7 +567,6 @@
cc_binary {
name: "adbd",
defaults: ["adbd_defaults", "host_adbd_supported", "libadbd_binary_dependencies"],
- stl: "libc++_static",
recovery_available: true,
apex_available: ["com.android.adbd"],
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 7fff05a..a663871 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -231,7 +231,14 @@
offset += write_size;
}
}
- SubmitWrites();
+
+ // Wake up the worker thread to submit writes.
+ uint64_t notify = 1;
+ ssize_t rc = adb_write(worker_event_fd_.get(), ¬ify, sizeof(notify));
+ if (rc < 0) {
+ PLOG(FATAL) << "failed to notify worker eventfd to submit writes";
+ }
+
return true;
}
@@ -443,6 +450,9 @@
}
ReadEvents();
+
+ std::lock_guard<std::mutex> lock(write_mutex_);
+ SubmitWrites();
}
});
}
@@ -626,8 +636,6 @@
write_requests_.erase(it);
size_t outstanding_writes = --writes_submitted_;
LOG(DEBUG) << "USB write: reaped, down to " << outstanding_writes;
-
- SubmitWrites();
}
IoWriteBlock CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset, size_t len,
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index a7cde68..cf0f1ac 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -117,8 +117,10 @@
"device/main.cpp",
"device/usb.cpp",
"device/usb_client.cpp",
+ "device/tcp_client.cpp",
"device/utility.cpp",
"device/variables.cpp",
+ "socket.cpp",
],
shared_libs: [
@@ -143,6 +145,7 @@
],
static_libs: [
+ "libgtest_prod",
"libhealthhalutils",
"libsnapshot_nobinder",
],
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index bb085c5..1b0859f 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -19,6 +19,7 @@
#include <algorithm>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/hardware/boot/1.0/IBootControl.h>
#include <android/hardware/fastboot/1.0/IFastboot.h>
@@ -28,6 +29,7 @@
#include "constants.h"
#include "flashing.h"
+#include "tcp_client.h"
#include "usb_client.h"
using android::fs_mgr::EnsurePathUnmounted;
@@ -60,11 +62,16 @@
{FB_CMD_GSI, GsiHandler},
{FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
}),
- transport_(std::make_unique<ClientUsbTransport>()),
boot_control_hal_(IBootControl::getService()),
health_hal_(get_health_service()),
fastboot_hal_(IFastboot::getService()),
active_slot_("") {
+ if (android::base::GetProperty("fastbootd.protocol", "usb") == "tcp") {
+ transport_ = std::make_unique<ClientTcpTransport>();
+ } else {
+ transport_ = std::make_unique<ClientUsbTransport>();
+ }
+
if (boot_control_hal_) {
boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
}
diff --git a/fastboot/device/tcp_client.cpp b/fastboot/device/tcp_client.cpp
new file mode 100644
index 0000000..ec5e1e3
--- /dev/null
+++ b/fastboot/device/tcp_client.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2020 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 "tcp_client.h"
+#include "constants.h"
+
+#include <android-base/errors.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+static constexpr int kDefaultPort = 5554;
+static constexpr int kProtocolVersion = 1;
+static constexpr int kHandshakeTimeoutMs = 2000;
+static constexpr size_t kHandshakeLength = 4;
+
+// Extract the big-endian 8-byte message length into a 64-bit number.
+static uint64_t ExtractMessageLength(const void* buffer) {
+ uint64_t ret = 0;
+ for (int i = 0; i < 8; ++i) {
+ ret |= uint64_t{reinterpret_cast<const uint8_t*>(buffer)[i]} << (56 - i * 8);
+ }
+ return ret;
+}
+
+// Encode the 64-bit number into a big-endian 8-byte message length.
+static void EncodeMessageLength(uint64_t length, void* buffer) {
+ for (int i = 0; i < 8; ++i) {
+ reinterpret_cast<uint8_t*>(buffer)[i] = length >> (56 - i * 8);
+ }
+}
+
+ClientTcpTransport::ClientTcpTransport() {
+ service_ = Socket::NewServer(Socket::Protocol::kTcp, kDefaultPort);
+
+ // A workaround to notify recovery to continue its work.
+ android::base::SetProperty("sys.usb.ffs.ready", "1");
+}
+
+ssize_t ClientTcpTransport::Read(void* data, size_t len) {
+ if (len > SSIZE_MAX) {
+ return -1;
+ }
+
+ size_t total_read = 0;
+ do {
+ // Read a new message
+ while (message_bytes_left_ == 0) {
+ if (socket_ == nullptr) {
+ ListenFastbootSocket();
+ }
+
+ char buffer[8];
+ if (socket_->ReceiveAll(buffer, 8, 0) == 8) {
+ message_bytes_left_ = ExtractMessageLength(buffer);
+ } else {
+ // If connection is closed by host, Receive will return 0 immediately.
+ socket_.reset(nullptr);
+ // In DATA phase, return error.
+ if (downloading_) {
+ return -1;
+ }
+ }
+ }
+
+ size_t read_length = len - total_read;
+ if (read_length > message_bytes_left_) {
+ read_length = message_bytes_left_;
+ }
+ ssize_t bytes_read =
+ socket_->ReceiveAll(reinterpret_cast<char*>(data) + total_read, read_length, 0);
+ if (bytes_read == -1) {
+ socket_.reset(nullptr);
+ return -1;
+ } else {
+ message_bytes_left_ -= bytes_read;
+ total_read += bytes_read;
+ }
+ // There are more than one DATA phases if the downloading buffer is too
+ // large, like a very big system image. All of data phases should be
+ // received until the whole buffer is filled in that case.
+ } while (downloading_ && total_read < len);
+
+ return total_read;
+}
+
+ssize_t ClientTcpTransport::Write(const void* data, size_t len) {
+ if (socket_ == nullptr || len > SSIZE_MAX) {
+ return -1;
+ }
+
+ // Use multi-buffer writes for better performance.
+ char header[8];
+ EncodeMessageLength(len, header);
+
+ if (!socket_->Send(std::vector<cutils_socket_buffer_t>{{header, 8}, {data, len}})) {
+ socket_.reset(nullptr);
+ return -1;
+ }
+
+ // In DATA phase
+ if (android::base::StartsWith(reinterpret_cast<const char*>(data), RESPONSE_DATA)) {
+ downloading_ = true;
+ } else {
+ downloading_ = false;
+ }
+
+ return len;
+}
+
+int ClientTcpTransport::Close() {
+ if (socket_ == nullptr) {
+ return -1;
+ }
+ socket_.reset(nullptr);
+
+ return 0;
+}
+
+int ClientTcpTransport::Reset() {
+ return Close();
+}
+
+void ClientTcpTransport::ListenFastbootSocket() {
+ while (true) {
+ socket_ = service_->Accept();
+
+ // Handshake
+ char buffer[kHandshakeLength + 1];
+ buffer[kHandshakeLength] = '\0';
+ if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) !=
+ kHandshakeLength) {
+ PLOG(ERROR) << "No Handshake message received";
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ if (memcmp(buffer, "FB", 2) != 0) {
+ PLOG(ERROR) << "Unrecognized initialization message";
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ int version = 0;
+ if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {
+ LOG(ERROR) << "Unknown TCP protocol version " << buffer + 2
+ << ", our version: " << kProtocolVersion;
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ std::string handshake_message(android::base::StringPrintf("FB%02d", kProtocolVersion));
+ if (!socket_->Send(handshake_message.c_str(), kHandshakeLength)) {
+ PLOG(ERROR) << "Failed to send initialization message";
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ break;
+ }
+}
diff --git a/fastboot/device/tcp_client.h b/fastboot/device/tcp_client.h
new file mode 100644
index 0000000..32e9834
--- /dev/null
+++ b/fastboot/device/tcp_client.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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 <memory>
+
+#include "socket.h"
+#include "transport.h"
+
+class ClientTcpTransport : public Transport {
+ public:
+ ClientTcpTransport();
+ ~ClientTcpTransport() override = default;
+
+ ssize_t Read(void* data, size_t len) override;
+ ssize_t Write(const void* data, size_t len) override;
+ int Close() override;
+ int Reset() override;
+
+ private:
+ void ListenFastbootSocket();
+
+ std::unique_ptr<Socket> service_;
+ std::unique_ptr<Socket> socket_;
+ uint64_t message_bytes_left_ = 0;
+ bool downloading_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientTcpTransport);
+};
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index e56ffcf..5a14b63 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -54,7 +54,9 @@
while (total < length) {
ssize_t bytes = Receive(reinterpret_cast<char*>(data) + total, length - total, timeout_ms);
- if (bytes == -1) {
+ // Returns 0 only when the peer has disconnected because our requested length is not 0. So
+ // we return immediately to avoid dead loop here.
+ if (bytes <= 0) {
if (total == 0) {
return -1;
}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 76e3955..b8385d3 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -454,7 +454,8 @@
<< entry.encryption_options;
return;
}
- if ((options.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) != 0) {
+ if ((options.flags &
+ (FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) != 0) {
// We can only use this policy on ext4 if the "stable_inodes" feature
// is set on the filesystem, otherwise shrinking will break encrypted files.
if ((sb->s_feature_compat & cpu_to_le32(EXT4_FEATURE_COMPAT_STABLE_INODES)) == 0) {
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index ea9c957..9046132 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -52,17 +52,27 @@
using DmTargetZero = android::dm::DmTargetZero;
using DmTargetLinear = android::dm::DmTargetLinear;
-static bool GetPhysicalPartitionDevicePath(const IPartitionOpener& opener,
- const LpMetadata& metadata,
+static bool GetPhysicalPartitionDevicePath(const CreateLogicalPartitionParams& params,
const LpMetadataBlockDevice& block_device,
const std::string& super_device, std::string* result) {
// If the super device is the source of this block device's metadata,
// make sure we use the correct super device (and not just "super",
// which might not exist.)
std::string name = GetBlockDevicePartitionName(block_device);
- std::string dev_string = opener.GetDeviceString(name);
- if (GetMetadataSuperBlockDevice(metadata) == &block_device) {
- dev_string = opener.GetDeviceString(super_device);
+ if (android::base::StartsWith(name, "dm-")) {
+ // Device-mapper nodes are not normally allowed in LpMetadata, since
+ // they are not consistent across reboots. However for the purposes of
+ // testing it's useful to handle them. For example when running DSUs,
+ // userdata is a device-mapper device, and some stacking will result
+ // when using libfiemap.
+ *result = "/dev/block/" + name;
+ return true;
+ }
+
+ auto opener = params.partition_opener;
+ std::string dev_string = opener->GetDeviceString(name);
+ if (GetMetadataSuperBlockDevice(*params.metadata) == &block_device) {
+ dev_string = opener->GetDeviceString(super_device);
}
// Note: device-mapper will not accept symlinks, so we must use realpath
@@ -93,8 +103,8 @@
case LP_TARGET_TYPE_LINEAR: {
const auto& block_device = params.metadata->block_devices[extent.target_source];
std::string dev_string;
- if (!GetPhysicalPartitionDevicePath(*params.partition_opener, *params.metadata,
- block_device, super_device, &dev_string)) {
+ if (!GetPhysicalPartitionDevicePath(params, block_device, super_device,
+ &dev_string)) {
LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
return false;
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index f3f1cb7..fa4ac39 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -402,16 +402,17 @@
return fstab_result;
}
-// Identify path to fstab file. Lookup is based on pattern fstab.<hardware>,
-// fstab.<hardware.platform> in folders /odm/etc, vendor/etc, or /.
+// Identify path to fstab file. Lookup is based on pattern
+// fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in
+// folders /odm/etc, vendor/etc, or /.
std::string GetFstabPath() {
- for (const char* prop : {"hardware", "hardware.platform"}) {
- std::string hw;
+ for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
+ std::string suffix;
- if (!fs_mgr_get_boot_config(prop, &hw)) continue;
+ if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
- std::string fstab_path = prefix + hw;
+ std::string fstab_path = prefix + suffix;
if (access(fstab_path.c_str(), F_OK) == 0) {
return fstab_path;
}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 673e145..7912688 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -29,6 +29,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <uuid/uuid.h>
@@ -140,6 +141,10 @@
return std::string{uuid_chars};
}
+static bool IsRecovery() {
+ return access("/system/bin/recovery", F_OK) == 0;
+}
+
bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
const std::chrono::milliseconds& timeout_ms) {
std::string uuid = GenerateUuid();
@@ -160,6 +165,16 @@
if (timeout_ms <= std::chrono::milliseconds::zero()) {
return true;
}
+
+ if (IsRecovery()) {
+ bool non_ab_device = android::base::GetProperty("ro.build.ab_update", "").empty();
+ int sdk = android::base::GetIntProperty("ro.build.version.sdk", 0);
+ if (non_ab_device && sdk && sdk <= 29) {
+ LOG(INFO) << "Detected ueventd incompatibility, reverting to legacy libdm behavior.";
+ unique_path = *path;
+ }
+ }
+
if (!WaitForFile(unique_path, timeout_ms)) {
LOG(ERROR) << "Failed waiting for device path: " << unique_path;
DeleteDevice(name);
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 6717922..3ee742f 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -51,6 +51,7 @@
using android::fs_mgr::GetPartitionName;
static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test";
+static constexpr char kOtaTestImageMetadataDir[] = "/metadata/gsi/ota/test";
std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix) {
auto metadata_dir = "/metadata/gsi/" + dir_prefix;
@@ -135,10 +136,13 @@
return !!FindPartition(*metadata.get(), name);
}
+static bool IsTestDir(const std::string& path) {
+ return android::base::StartsWith(path, kTestImageMetadataDir) ||
+ android::base::StartsWith(path, kOtaTestImageMetadataDir);
+}
+
static bool IsUnreliablePinningAllowed(const std::string& path) {
- return android::base::StartsWith(path, "/data/gsi/dsu/") ||
- android::base::StartsWith(path, "/data/gsi/test/") ||
- android::base::StartsWith(path, "/data/gsi/ota/test/");
+ return android::base::StartsWith(path, "/data/gsi/dsu/") || IsTestDir(path);
}
FiemapStatus ImageManager::CreateBackingImage(
@@ -174,8 +178,7 @@
// if device-mapper is stacked in some complex way not supported by
// FiemapWriter.
auto device_path = GetDevicePathForFile(fw.get());
- if (android::base::StartsWith(device_path, "/dev/block/dm-") &&
- !android::base::StartsWith(metadata_dir_, kTestImageMetadataDir)) {
+ if (android::base::StartsWith(device_path, "/dev/block/dm-") && !IsTestDir(metadata_dir_)) {
LOG(ERROR) << "Cannot persist images against device-mapper device: " << device_path;
fw = {};
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 2ac0c44..0328132 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -132,7 +132,7 @@
uint64 metadata_sectors = 4;
}
-// Next: 2
+// Next: 4
message SnapshotMergeReport {
// Status of the update after the merge attempts.
UpdateState state = 1;
@@ -140,4 +140,7 @@
// Number of reboots that occurred after issuing and before completeing the
// merge of all the snapshot devices.
int32 resume_count = 2;
+
+ // Total size of all the COW images before the update.
+ uint64 cow_file_size = 3;
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index ac8a25e..b207978 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -148,7 +148,7 @@
// Initiate a merge on all snapshot devices. This should only be used after an
// update has been marked successful after booting.
- bool InitiateMerge();
+ bool InitiateMerge(uint64_t* cow_file_size = nullptr);
// Perform any necessary post-boot actions. This should be run soon after
// /data is mounted.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 91dd34f..bdc3ea3 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -31,6 +31,8 @@
// Called when merge starts or resumes.
bool Start();
void set_state(android::snapshot::UpdateState state);
+ virtual void set_cow_file_size(uint64_t cow_file_size);
+ virtual uint64_t cow_file_size();
// Called when merge ends. Properly clean up permanent storage.
class Result {
@@ -43,6 +45,8 @@
std::unique_ptr<Result> Finish();
private:
+ virtual ~SnapshotMergeStats() {}
+
bool ReadState();
bool WriteState();
bool DeleteState();
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index d4a3f62..0739fab 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -555,7 +555,7 @@
return true;
}
-bool SnapshotManager::InitiateMerge() {
+bool SnapshotManager::InitiateMerge(uint64_t* cow_file_size) {
auto lock = LockExclusive();
if (!lock) return false;
@@ -618,6 +618,7 @@
}
}
+ uint64_t total_cow_file_size = 0;
DmTargetSnapshot::Status initial_target_values = {};
for (const auto& snapshot : snapshots) {
DmTargetSnapshot::Status current_status;
@@ -627,6 +628,16 @@
initial_target_values.sectors_allocated += current_status.sectors_allocated;
initial_target_values.total_sectors += current_status.total_sectors;
initial_target_values.metadata_sectors += current_status.metadata_sectors;
+
+ SnapshotStatus snapshot_status;
+ if (!ReadSnapshotStatus(lock.get(), snapshot, &snapshot_status)) {
+ return false;
+ }
+ total_cow_file_size += snapshot_status.cow_file_size();
+ }
+
+ if (cow_file_size) {
+ *cow_file_size = total_cow_file_size;
}
SnapshotUpdateStatus initial_status;
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 5da7b98..3723730 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -88,6 +88,15 @@
report_.set_state(state);
}
+void SnapshotMergeStats::set_cow_file_size(uint64_t cow_file_size) {
+ report_.set_cow_file_size(cow_file_size);
+ WriteState();
+}
+
+uint64_t SnapshotMergeStats::cow_file_size() {
+ return report_.cow_file_size();
+}
+
class SnapshotMergeStatsResultImpl : public SnapshotMergeStats::Result {
public:
SnapshotMergeStatsResultImpl(const SnapshotMergeReport& report,
diff --git a/init/README.md b/init/README.md
index 726c0cc..0dd1490 100644
--- a/init/README.md
+++ b/init/README.md
@@ -547,13 +547,16 @@
* `ref`: use the systemwide DE key
* `per_boot_ref`: use the key freshly generated on each boot.
-`mount_all <fstab> [ <path> ]\* [--<option>]`
+`mount_all [ <fstab> ] [--<option>]`
> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab with optional
options "early" and "late".
With "--early" set, the init executable will skip mounting entries with
"latemount" flag and triggering fs encryption state event. With "--late" set,
init executable will only mount entries with "latemount" flag. By default,
no option is set, and mount\_all will process all entries in the given fstab.
+ If the fstab parameter is not specified, fstab.${ro.boot.fstab_suffix},
+ fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for
+ under /odm/etc, /vendor/etc, or / at runtime, in that order.
`mount <type> <device> <dir> [ <flag>\* ] [<options>]`
> Attempt to mount the named device at the directory _dir_
@@ -644,7 +647,8 @@
`wait <path> [ <timeout> ]`
> Poll for the existence of the given file and return when found,
or the timeout has been reached. If timeout is not specified it
- currently defaults to five seconds.
+ currently defaults to five seconds. The timeout value can be
+ fractional seconds, specified in floating point notation.
`wait_for_prop <name> <value>`
> Wait for system property _name_ to be _value_. Properties are expanded
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 200bfff..e918e12 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -49,6 +49,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -517,21 +518,21 @@
/* Imports .rc files from the specified paths. Default ones are applied if none is given.
*
- * start_index: index of the first path in the args list
+ * rc_paths: list of paths to rc files to import
*/
-static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
+static void import_late(const std::vector<std::string>& rc_paths) {
auto& action_manager = ActionManager::GetInstance();
auto& service_list = ServiceList::GetInstance();
Parser parser = CreateParser(action_manager, service_list);
- if (end_index <= start_index) {
+ if (rc_paths.empty()) {
// Fallbacks for partitions on which early mount isn't enabled.
for (const auto& path : late_import_paths) {
parser.ParseConfig(path);
}
late_import_paths.clear();
} else {
- for (size_t i = start_index; i < end_index; ++i) {
- parser.ParseConfig(args[i]);
+ for (const auto& rc_path : rc_paths) {
+ parser.ParseConfig(rc_path);
}
}
@@ -632,48 +633,44 @@
static int initial_mount_fstab_return_code = -1;
-/* mount_all <fstab> [ <path> ]* [--<options>]*
+/* <= Q: mount_all <fstab> [ <path> ]* [--<options>]*
+ * >= R: mount_all [ <fstab> ] [--<options>]*
*
* This function might request a reboot, in which case it will
* not return.
*/
static Result<void> do_mount_all(const BuiltinArguments& args) {
- std::size_t na = 0;
- bool import_rc = true;
- bool queue_event = true;
- int mount_mode = MOUNT_MODE_DEFAULT;
- const auto& fstab_file = args[1];
- std::size_t path_arg_end = args.size();
- const char* prop_post_fix = "default";
+ auto mount_all = ParseMountAll(args.args);
+ if (!mount_all.ok()) return mount_all.error();
- for (na = args.size() - 1; na > 1; --na) {
- if (args[na] == "--early") {
- path_arg_end = na;
- queue_event = false;
- mount_mode = MOUNT_MODE_EARLY;
- prop_post_fix = "early";
- } else if (args[na] == "--late") {
- path_arg_end = na;
- import_rc = false;
- mount_mode = MOUNT_MODE_LATE;
- prop_post_fix = "late";
- }
+ const char* prop_post_fix = "default";
+ bool queue_event = true;
+ if (mount_all->mode == MOUNT_MODE_EARLY) {
+ prop_post_fix = "early";
+ queue_event = false;
+ } else if (mount_all->mode == MOUNT_MODE_LATE) {
+ prop_post_fix = "late";
}
std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
android::base::Timer t;
Fstab fstab;
- if (!ReadFstabFromFile(fstab_file, &fstab)) {
- return Error() << "Could not read fstab";
+ if (mount_all->fstab_path.empty()) {
+ if (!ReadDefaultFstab(&fstab)) {
+ return Error() << "Could not read default fstab";
+ }
+ } else {
+ if (!ReadFstabFromFile(mount_all->fstab_path, &fstab)) {
+ return Error() << "Could not read fstab";
+ }
}
- auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
+ auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_all->mode);
SetProperty(prop_name, std::to_string(t.duration().count()));
- if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
- /* Paths of .rc files are specified at the 2nd argument and beyond */
- import_late(args.args, 2, path_arg_end);
+ if (mount_all->import_rc) {
+ import_late(mount_all->rc_paths);
}
if (queue_event) {
@@ -689,11 +686,20 @@
return {};
}
-/* umount_all <fstab> */
+/* umount_all [ <fstab> ] */
static Result<void> do_umount_all(const BuiltinArguments& args) {
+ auto umount_all = ParseUmountAll(args.args);
+ if (!umount_all.ok()) return umount_all.error();
+
Fstab fstab;
- if (!ReadFstabFromFile(args[1], &fstab)) {
- return Error() << "Could not read fstab";
+ if (umount_all->empty()) {
+ if (!ReadDefaultFstab(&fstab)) {
+ return Error() << "Could not read default fstab";
+ }
+ } else {
+ if (!ReadFstabFromFile(*umount_all, &fstab)) {
+ return Error() << "Could not read fstab";
+ }
}
if (auto result = fs_mgr_umount_all(&fstab); result != 0) {
@@ -1065,11 +1071,12 @@
static Result<void> do_wait(const BuiltinArguments& args) {
auto timeout = kCommandRetryTimeout;
if (args.size() == 3) {
- int timeout_int;
- if (!android::base::ParseInt(args[2], &timeout_int)) {
+ double timeout_double;
+ if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {
return Error() << "failed to parse timeout";
}
- timeout = std::chrono::seconds(timeout_int);
+ timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(
+ std::chrono::duration<double>(timeout_double));
}
if (wait_for_file(args[1].c_str(), timeout) != 0) {
@@ -1347,11 +1354,11 @@
// mount_all is currently too complex to run in vendor_init as it queues action triggers,
// imports rc scripts, etc. It should be simplified and run in vendor_init context.
// mount and umount are run in the same context as mount_all for symmetry.
- {"mount_all", {1, kMax, {false, do_mount_all}}},
+ {"mount_all", {0, kMax, {false, do_mount_all}}},
{"mount", {3, kMax, {false, do_mount}}},
{"perform_apex_config", {0, 0, {false, do_perform_apex_config}}},
{"umount", {1, 1, {false, do_umount}}},
- {"umount_all", {1, 1, {false, do_umount_all}}},
+ {"umount_all", {0, 1, {false, do_umount_all}}},
{"update_linker_config", {0, 0, {false, do_update_linker_config}}},
{"readahead", {1, 2, {true, do_readahead}}},
{"remount_userdata", {0, 0, {false, do_remount_userdata}}},
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index d62ecb0..450c079 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -25,6 +25,7 @@
#include <sys/time.h>
#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
@@ -122,6 +123,14 @@
return {};
}
+Result<void> check_mount_all(const BuiltinArguments& args) {
+ auto options = ParseMountAll(args.args);
+ if (!options.ok()) {
+ return options.error();
+ }
+ return {};
+}
+
Result<void> check_mkdir(const BuiltinArguments& args) {
auto options = ParseMkdir(args.args);
if (!options.ok()) {
@@ -203,10 +212,18 @@
return {};
}
+Result<void> check_umount_all(const BuiltinArguments& args) {
+ auto options = ParseUmountAll(args.args);
+ if (!options.ok()) {
+ return options.error();
+ }
+ return {};
+}
+
Result<void> check_wait(const BuiltinArguments& args) {
if (args.size() == 3 && !args[2].empty()) {
- int timeout_int;
- if (!android::base::ParseInt(args[2], &timeout_int)) {
+ double timeout_double;
+ if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {
return Error() << "failed to parse timeout";
}
}
diff --git a/init/check_builtins.h b/init/check_builtins.h
index fb34556..725a6fd 100644
--- a/init/check_builtins.h
+++ b/init/check_builtins.h
@@ -32,11 +32,13 @@
Result<void> check_load_system_props(const BuiltinArguments& args);
Result<void> check_loglevel(const BuiltinArguments& args);
Result<void> check_mkdir(const BuiltinArguments& args);
+Result<void> check_mount_all(const BuiltinArguments& args);
Result<void> check_restorecon(const BuiltinArguments& args);
Result<void> check_restorecon_recursive(const BuiltinArguments& args);
Result<void> check_setprop(const BuiltinArguments& args);
Result<void> check_setrlimit(const BuiltinArguments& args);
Result<void> check_sysclktz(const BuiltinArguments& args);
+Result<void> check_umount_all(const BuiltinArguments& args);
Result<void> check_wait(const BuiltinArguments& args);
Result<void> check_wait_for_prop(const BuiltinArguments& args);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index ffd58a3..23a07aa 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -678,9 +678,12 @@
// Reap subcontext pids.
ReapAnyOutstandingChildren();
- // 3. send volume shutdown to vold
+ // 3. send volume abort_fuse and volume shutdown to vold
Service* vold_service = ServiceList::GetInstance().FindService("vold");
if (vold_service != nullptr && vold_service->IsRunning()) {
+ // Manually abort FUSE connections, since the FUSE daemon is already dead
+ // at this point, and unmounting it might hang.
+ CallVdc("volume", "abort_fuse");
CallVdc("volume", "shutdown");
vold_service->Stop();
} else {
diff --git a/init/util.cpp b/init/util.cpp
index 24f94ec..90ac50c 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -570,6 +570,48 @@
return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
}
+Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args) {
+ bool compat_mode = false;
+ bool import_rc = false;
+ if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
+ if (args.size() <= 1) {
+ return Error() << "mount_all requires at least 1 argument";
+ }
+ compat_mode = true;
+ import_rc = true;
+ }
+
+ std::size_t first_option_arg = args.size();
+ enum mount_mode mode = MOUNT_MODE_DEFAULT;
+
+ // If we are <= Q, then stop looking for non-fstab arguments at slot 2.
+ // Otherwise, stop looking at slot 1 (as the fstab path argument is optional >= R).
+ for (std::size_t na = args.size() - 1; na > (compat_mode ? 1 : 0); --na) {
+ if (args[na] == "--early") {
+ first_option_arg = na;
+ mode = MOUNT_MODE_EARLY;
+ } else if (args[na] == "--late") {
+ first_option_arg = na;
+ mode = MOUNT_MODE_LATE;
+ import_rc = false;
+ }
+ }
+
+ std::string fstab_path;
+ if (first_option_arg > 1) {
+ fstab_path = args[1];
+ } else if (compat_mode) {
+ return Error() << "mount_all argument 1 must be the fstab path";
+ }
+
+ std::vector<std::string> rc_paths;
+ for (std::size_t na = 2; na < first_option_arg; ++na) {
+ rc_paths.push_back(args[na]);
+ }
+
+ return MountAllOptions{rc_paths, fstab_path, mode, import_rc};
+}
+
Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
const std::vector<std::string>& args) {
struct flag_type {
@@ -610,6 +652,15 @@
return std::pair(flag, paths);
}
+Result<std::string> ParseUmountAll(const std::vector<std::string>& args) {
+ if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
+ if (args.size() <= 1) {
+ return Error() << "umount_all requires at least 1 argument";
+ }
+ }
+ return args[1];
+}
+
static void InitAborter(const char* abort_message) {
// When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
// simply abort instead of trying to reboot the system.
diff --git a/init/util.h b/init/util.h
index ad322d9..a7f813b 100644
--- a/init/util.h
+++ b/init/util.h
@@ -22,6 +22,7 @@
#include <chrono>
#include <functional>
#include <string>
+#include <vector>
#include <android-base/chrono_utils.h>
@@ -33,6 +34,12 @@
namespace android {
namespace init {
+enum mount_mode {
+ MOUNT_MODE_DEFAULT = 0,
+ MOUNT_MODE_EARLY = 1,
+ MOUNT_MODE_LATE = 2,
+};
+
static const char kColdBootDoneProp[] = "ro.cold_boot_done";
extern void (*trigger_shutdown)(const std::string& command);
@@ -73,9 +80,20 @@
Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args);
+struct MountAllOptions {
+ std::vector<std::string> rc_paths;
+ std::string fstab_path;
+ mount_mode mode;
+ bool import_rc;
+};
+
+Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args);
+
Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
const std::vector<std::string>& args);
+Result<std::string> ParseUmountAll(const std::vector<std::string>& args);
+
void SetStdioToDevNull(char** argv);
void InitKernelLogging(char** argv);
bool IsRecoveryMode();
diff --git a/libstats/pull/Android.bp b/libstats/pull/Android.bp
index 2658639..a8b4a4f 100644
--- a/libstats/pull/Android.bp
+++ b/libstats/pull/Android.bp
@@ -85,13 +85,18 @@
"libstatssocket",
],
test_suites: ["general-tests", "mts"],
+ test_config: "libstatspull_test.xml",
+
//TODO(b/153588990): Remove when the build system properly separates
//32bit and 64bit architectures.
compile_multilib: "both",
multilib: {
lib64: {
suffix: "64",
- }
+ },
+ lib32: {
+ suffix: "32",
+ },
},
cflags: [
"-Wall",
diff --git a/libstats/pull/libstatspull_test.xml b/libstats/pull/libstatspull_test.xml
new file mode 100644
index 0000000..233fc1f
--- /dev/null
+++ b/libstats/pull/libstatspull_test.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs libstatspull_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+ <option name="test-suite-tag" value="mts" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="libstatspull_test->/data/local/tmp/libstatspull_test" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="libstatspull_test" />
+ </test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
+ </object>
+</configuration>
diff --git a/libstats/pull/stats_pull_atom_callback.cpp b/libstats/pull/stats_pull_atom_callback.cpp
index bc34c63..478cae7 100644
--- a/libstats/pull/stats_pull_atom_callback.cpp
+++ b/libstats/pull/stats_pull_atom_callback.cpp
@@ -47,7 +47,7 @@
}
static const int64_t DEFAULT_COOL_DOWN_MILLIS = 1000LL; // 1 second.
-static const int64_t DEFAULT_TIMEOUT_MILLIS = 10000LL; // 10 seconds.
+static const int64_t DEFAULT_TIMEOUT_MILLIS = 2000LL; // 2 seconds.
struct AStatsManager_PullAtomMetadata {
int64_t cool_down_millis;
diff --git a/libstats/pull/tests/pull_atom_metadata_test.cpp b/libstats/pull/tests/pull_atom_metadata_test.cpp
index cf19303..abc8e47 100644
--- a/libstats/pull/tests/pull_atom_metadata_test.cpp
+++ b/libstats/pull/tests/pull_atom_metadata_test.cpp
@@ -21,7 +21,7 @@
namespace {
static const int64_t DEFAULT_COOL_DOWN_MILLIS = 1000LL; // 1 second.
-static const int64_t DEFAULT_TIMEOUT_MILLIS = 10000LL; // 10 seconds.
+static const int64_t DEFAULT_TIMEOUT_MILLIS = 2000LL; // 2 seconds.
} // anonymous namespace
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index 4e89b94..2bf0261 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -127,13 +127,17 @@
"libutils",
],
test_suites: ["device-tests", "mts"],
+ test_config: "libstatssocket_test.xml",
//TODO(b/153588990): Remove when the build system properly separates
//32bit and 64bit architectures.
compile_multilib: "both",
multilib: {
lib64: {
suffix: "64",
- }
+ },
+ lib32: {
+ suffix: "32",
+ },
},
require_root: true,
}
diff --git a/libstats/socket/libstatssocket_test.xml b/libstats/socket/libstatssocket_test.xml
new file mode 100644
index 0000000..d2694d1
--- /dev/null
+++ b/libstats/socket/libstatssocket_test.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs libstatssocket_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+ <option name="test-suite-tag" value="mts" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="libstatssocket_test->/data/local/tmp/libstatssocket_test" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="libstatssocket_test" />
+ </test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
+ </object>
+</configuration>
+
diff --git a/libstats/socket/stats_buffer_writer.c b/libstats/socket/stats_buffer_writer.c
index 74acb20..549acdc 100644
--- a/libstats/socket/stats_buffer_writer.c
+++ b/libstats/socket/stats_buffer_writer.c
@@ -50,24 +50,16 @@
int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId) {
int ret = 1;
-#ifdef __ANDROID__
- bool statsdEnabled = property_get_bool("ro.statsd.enable", true);
-#else
- bool statsdEnabled = false;
-#endif
+ struct iovec vecs[2];
+ vecs[0].iov_base = (void*)&kStatsEventTag;
+ vecs[0].iov_len = sizeof(kStatsEventTag);
+ vecs[1].iov_base = buffer;
+ vecs[1].iov_len = size;
- if (statsdEnabled) {
- struct iovec vecs[2];
- vecs[0].iov_base = (void*)&kStatsEventTag;
- vecs[0].iov_len = sizeof(kStatsEventTag);
- vecs[1].iov_base = buffer;
- vecs[1].iov_len = size;
+ ret = __write_to_statsd(vecs, 2);
- ret = __write_to_statsd(vecs, 2);
-
- if (ret < 0) {
- note_log_drop(ret, atomId);
- }
+ if (ret < 0) {
+ note_log_drop(ret, atomId);
}
return ret;
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 1d899ab..0abb861 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -189,7 +189,11 @@
int adjust = offset % mPageSize;
off64_t adjOffset = offset - adjust;
- size_t adjLength = length + adjust;
+ size_t adjLength;
+ if (__builtin_add_overflow(length, adjust, &adjLength)) {
+ ALOGE("adjusted length overflow: length %zu adjust %d", length, adjust);
+ return false;
+ }
int flags = MAP_SHARED;
int prot = PROT_READ;
diff --git a/libutils/FileMap_test.cpp b/libutils/FileMap_test.cpp
index 9f7ce85..fd1c9b0 100644
--- a/libutils/FileMap_test.cpp
+++ b/libutils/FileMap_test.cpp
@@ -52,3 +52,16 @@
ASSERT_EQ(0u, m.getDataLength());
ASSERT_EQ(offset, m.getDataOffset());
}
+
+TEST(FileMap, offset_overflow) {
+ // Make sure that an end that overflows SIZE_MAX will not abort.
+ // See http://b/156997193.
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ off64_t offset = 200;
+ size_t length = SIZE_MAX;
+
+ android::FileMap m;
+ ASSERT_FALSE(m.create("test", tf.fd, offset, length, true));
+}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 38ba137..2d67194 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -177,6 +177,9 @@
mount binder binder /dev/binderfs stats=global
chmod 0755 /dev/binderfs
+ # Mount fusectl
+ mount fusectl none /sys/fs/fuse/connections
+
symlink /dev/binderfs/binder /dev/binder
symlink /dev/binderfs/hwbinder /dev/hwbinder
symlink /dev/binderfs/vndbinder /dev/vndbinder
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 9adbcba..e827cf5 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,6 +5,7 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
+ onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index f6149c9..fe6dcfa 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,6 +5,7 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
+ onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 0e69b16..adc7031 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,6 +5,7 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
+ onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 3e80168..7029748 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,6 +5,7 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
+ onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver