Merge "liblp_test_static: fix test"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index a3bd44f..cc1978d 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,6 +4,9 @@
"name": "adbd_test"
},
{
+ "name": "CtsInitTestCases"
+ },
+ {
"name": "debuggerd_test"
},
{
@@ -13,9 +16,6 @@
"name": "fs_mgr_vendor_overlay_test"
},
{
- "name": "init_tests"
- },
- {
"name": "libbase_test"
},
{
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index a4e0d76..e7a3ff2 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -79,6 +79,13 @@
return true;
}
+bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
+ if (GetState(name) == DmDeviceState::INVALID) {
+ return true;
+ }
+ return DeleteDevice(name);
+}
+
bool DeviceMapper::DeleteDevice(const std::string& name) {
struct dm_ioctl io;
InitIo(&io, name);
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index cf306f3..e25ce7f 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -89,6 +89,7 @@
// Removes a device mapper device with the given name.
// Returns 'true' on success, false otherwise.
bool DeleteDevice(const std::string& name);
+ bool DeleteDeviceIfExists(const std::string& name);
// Fetches and returns the complete state of the underlying device mapper
// device with given name.
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index f1e8fc2..1d4db85 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -41,7 +41,21 @@
if (android::base::StartsWith(path, "/")) {
return path;
}
- return "/dev/block/by-name/" + path;
+
+ auto by_name = "/dev/block/by-name/" + path;
+ if (access(by_name.c_str(), F_OK) != 0) {
+ // If the by-name symlink doesn't exist, as a special case we allow
+ // certain devices to be used as partition names. This can happen if a
+ // Dynamic System Update is installed to an sdcard, which won't be in
+ // the boot device list.
+ //
+ // We whitelist because most devices in /dev/block are not valid for
+ // storing fiemaps.
+ if (android::base::StartsWith(path, "mmcblk")) {
+ return "/dev/block/" + path;
+ }
+ }
+ return by_name;
}
bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 128a4f9..51f5c50 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -74,6 +74,7 @@
defaults: ["libsnapshot_defaults"],
srcs: [
"snapshot_test.cpp",
+ "test_helpers.cpp",
],
shared_libs: [
"libbinder",
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 438ec2f..518c619 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -26,9 +26,13 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr_dm_linear.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
+#include <liblp/builder.h>
+
+#include "test_helpers.h"
namespace android {
namespace snapshot {
@@ -37,25 +41,35 @@
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
using android::fiemap::IImageManager;
+using android::fs_mgr::BlockDeviceInfo;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::MetadataBuilder;
using namespace std::chrono_literals;
using namespace std::string_literals;
-class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
- public:
- 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_; }
-
- void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
-
- private:
- std::string slot_suffix_ = "_a";
-};
-
-// These are not reset between each test because it's expensive to reconnect
-// to gsid each time.
+// These are not reset between each test because it's expensive to create
+// these resources (starting+connecting to gsid, zero-filling images).
std::unique_ptr<SnapshotManager> sm;
TestDeviceInfo* test_device = nullptr;
+std::string fake_super;
+
+static constexpr uint64_t kSuperSize = 16 * 1024 * 1024;
+
+// Helper to remove stale partitions in fake super.
+void CleanupPartitions() {
+ // These are hardcoded since we might abort in the middle of a test, and
+ // can't recover the in-use list.
+ static std::vector<std::string> kPartitionNames = {
+ "base-device",
+ };
+
+ auto& dm = DeviceMapper::Instance();
+ for (const auto& partition : kPartitionNames) {
+ if (dm.GetState(partition) != DmDeviceState::INVALID) {
+ dm.DeleteDevice(partition);
+ }
+ }
+}
class SnapshotTest : public ::testing::Test {
public:
@@ -69,6 +83,7 @@
test_device->set_slot_suffix("_a");
CleanupTestArtifacts();
+ FormatFakeSuper();
ASSERT_TRUE(sm->BeginUpdate());
}
@@ -96,12 +111,7 @@
android::base::RemoveFileIfExists(status_file);
}
- // Remove all images. We hardcode the list of names since this can run
- // before the test (when cleaning up from a crash).
- std::vector<std::string> temp_partitions = {"base-device"};
- for (const auto& partition : temp_partitions) {
- DeleteBackingImage(image_manager_, partition);
- }
+ CleanupPartitions();
if (sm->GetUpdateState() != UpdateState::None) {
auto state_file = sm->GetStateFilePath();
@@ -114,11 +124,48 @@
return !!lock_;
}
- bool CreateTempDevice(const std::string& name, uint64_t size, std::string* path) {
- if (!image_manager_->CreateBackingImage(name, size, false)) {
+ void FormatFakeSuper() {
+ BlockDeviceInfo super_device("super", kSuperSize, 0, 0, 4096);
+ std::vector<BlockDeviceInfo> devices = {super_device};
+
+ auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ TestPartitionOpener opener(fake_super);
+ ASSERT_TRUE(FlashPartitionTable(opener, fake_super, *metadata.get()));
+ }
+
+ // If |path| is non-null, the partition will be mapped after creation.
+ bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr) {
+ TestPartitionOpener opener(fake_super);
+ auto builder = MetadataBuilder::New(opener, "super", 0);
+ if (!builder) return false;
+
+ auto partition = builder->AddPartition(name, 0);
+ if (!partition) return false;
+ if (!builder->ResizePartition(partition, size)) {
return false;
}
- return image_manager_->MapImageDevice(name, 10s, path);
+
+ auto metadata = builder->Export();
+ if (!metadata) return false;
+ if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) {
+ return false;
+ }
+
+ if (!path) return true;
+
+ CreateLogicalPartitionParams params = {
+ .block_device = fake_super,
+ .metadata = metadata.get(),
+ .partition_name = name,
+ .force_writable = true,
+ .timeout_ms = 10s,
+ };
+ return CreateLogicalPartition(params, path);
}
void DeleteSnapshotDevice(const std::string& snapshot) {
@@ -130,18 +177,10 @@
}
}
- void DeleteBackingImage(IImageManager* manager, const std::string& name) {
- if (manager->IsImageMapped(name)) {
- ASSERT_TRUE(manager->UnmapImageDevice(name));
- }
- if (manager->BackingImageExists(name)) {
- ASSERT_TRUE(manager->DeleteBackingImage(name));
- }
- }
-
DeviceMapper& dm_;
std::unique_ptr<SnapshotManager::LockedFile> lock_;
android::fiemap::IImageManager* image_manager_ = nullptr;
+ std::string fake_super_;
};
TEST_F(SnapshotTest, CreateSnapshot) {
@@ -177,7 +216,7 @@
kDeviceSize));
std::string base_device;
- ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
+ ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
std::string snap_device;
ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
@@ -193,7 +232,7 @@
kSnapshotSize));
std::string base_device;
- ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
+ ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
std::string snap_device;
ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
@@ -215,7 +254,7 @@
kDeviceSize));
std::string base_device, snap_device;
- ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
+ ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
std::string test_string = "This is a test string.";
@@ -270,7 +309,7 @@
kDeviceSize));
std::string base_device, snap_device;
- ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
+ ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
// Keep an open handle to the cow device. This should cause the merge to
@@ -321,10 +360,14 @@
::testing::InitGoogleTest(&argc, argv);
std::vector<std::string> paths = {
+ // clang-format off
"/data/gsi/ota/test",
+ "/data/gsi/ota/test/super",
"/metadata/gsi/ota/test",
+ "/metadata/gsi/ota/test/super",
"/metadata/ota/test",
"/metadata/ota/test/snapshots",
+ // clang-format on
};
for (const auto& path : paths) {
if (!Mkdir(path)) {
@@ -336,9 +379,38 @@
test_device = new TestDeviceInfo();
sm = SnapshotManager::New(test_device);
if (!sm) {
- std::cerr << "Could not create snapshot manager";
+ std::cerr << "Could not create snapshot manager\n";
return 1;
}
- return RUN_ALL_TESTS();
+ // Use a separate image manager for our fake super partition.
+ auto super_images = IImageManager::Open("ota/test/super", 10s);
+ if (!super_images) {
+ std::cerr << "Could not create image manager\n";
+ return 1;
+ }
+
+ // Clean up previous run.
+ CleanupPartitions();
+ DeleteBackingImage(super_images.get(), "fake-super");
+
+ // Create and map the fake super partition.
+ static constexpr int kImageFlags =
+ IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
+ if (!super_images->CreateBackingImage("fake-super", kSuperSize, kImageFlags)) {
+ std::cerr << "Could not create fake super partition\n";
+ return 1;
+ }
+ if (!super_images->MapImageDevice("fake-super", 10s, &fake_super)) {
+ std::cerr << "Could not map fake super partition\n";
+ return 1;
+ }
+
+ auto result = RUN_ALL_TESTS();
+
+ // Clean up again.
+ CleanupPartitions();
+ DeleteBackingImage(super_images.get(), "fake-super");
+
+ return result;
}
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
new file mode 100644
index 0000000..17ffa4e
--- /dev/null
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -0,0 +1,50 @@
+// 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 "test_helpers.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace snapshot {
+
+using android::fiemap::IImageManager;
+
+void DeleteBackingImage(IImageManager* manager, const std::string& name) {
+ if (manager->IsImageMapped(name)) {
+ ASSERT_TRUE(manager->UnmapImageDevice(name));
+ }
+ if (manager->BackingImageExists(name)) {
+ ASSERT_TRUE(manager->DeleteBackingImage(name));
+ }
+}
+
+android::base::unique_fd TestPartitionOpener::Open(const std::string& partition_name,
+ int flags) const {
+ if (partition_name == "super") {
+ return PartitionOpener::Open(fake_super_path_, flags);
+ }
+ return PartitionOpener::Open(partition_name, flags);
+}
+
+bool TestPartitionOpener::GetInfo(const std::string& partition_name,
+ android::fs_mgr::BlockDeviceInfo* info) const {
+ if (partition_name == "super") {
+ return PartitionOpener::GetInfo(fake_super_path_, info);
+ }
+ return PartitionOpener::GetInfo(partition_name, info);
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
new file mode 100644
index 0000000..9491be3
--- /dev/null
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -0,0 +1,58 @@
+// 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 <libfiemap/image_manager.h>
+#include <liblp/partition_opener.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace std::string_literals;
+
+class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
+ public:
+ 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_; }
+
+ void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
+
+ private:
+ std::string slot_suffix_ = "_a";
+};
+
+// Redirect requests for "super" to our fake super partition.
+class TestPartitionOpener final : public android::fs_mgr::PartitionOpener {
+ public:
+ explicit TestPartitionOpener(const std::string& fake_super_path)
+ : fake_super_path_(fake_super_path) {}
+
+ android::base::unique_fd Open(const std::string& partition_name, int flags) const override;
+ bool GetInfo(const std::string& partition_name,
+ android::fs_mgr::BlockDeviceInfo* info) const override;
+
+ private:
+ std::string fake_super_path_;
+};
+
+// Helper for error-spam-free cleanup.
+void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name);
+
+} // namespace snapshot
+} // namespace android
diff --git a/init/Android.bp b/init/Android.bp
index 38d495f..7dfb828 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -128,6 +128,7 @@
"persistent_properties.cpp",
"persistent_properties.proto",
"property_service.cpp",
+ "property_service.proto",
"property_type.cpp",
"reboot.cpp",
"reboot_utils.cpp",
@@ -206,9 +207,18 @@
// ------------------------------------------------------------------------------
cc_test {
- name: "init_tests",
+ name: "CtsInitTestCases",
defaults: ["init_defaults"],
- compile_multilib: "first",
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
srcs: [
"devices_test.cpp",
"init_test.cpp",
@@ -225,7 +235,12 @@
"util_test.cpp",
],
static_libs: ["libinit"],
- test_suites: ["device-tests"],
+
+ test_suites: [
+ "cts",
+ "device-tests",
+ "vts",
+ ],
}
cc_benchmark {
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
new file mode 100644
index 0000000..94a02e6
--- /dev/null
+++ b/init/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for CTS init test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="systems" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
+ <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="CtsInitTestCases" />
+ <option name="runtime-hint" value="65s" />
+ </test>
+</configuration>
diff --git a/init/builtins.cpp b/init/builtins.cpp
index a2d782b..7076926 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::StartsWith;
using android::base::unique_fd;
using android::fs_mgr::Fstab;
using android::fs_mgr::ReadFstabFromFile;
@@ -640,12 +641,7 @@
if (!ReadFstabFromFile(fstab_file, &fstab)) {
return Error() << "Could not read fstab";
}
-
- auto mount_fstab_return_code =
- CallFunctionAndHandleProperties(fs_mgr_mount_all, &fstab, mount_mode);
- if (!mount_fstab_return_code) {
- return Error() << "Could not call fs_mgr_mount_all(): " << mount_fstab_return_code.error();
- }
+ auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
property_set(prop_name, std::to_string(t.duration().count()));
if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
@@ -656,7 +652,7 @@
if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code
* and return processed return code*/
- auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
+ auto queue_fs_result = queue_fs_event(mount_fstab_return_code);
if (!queue_fs_result) {
return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
}
@@ -672,13 +668,8 @@
return Error() << "Could not read fstab";
}
- auto result = CallFunctionAndHandleProperties(fs_mgr_umount_all, &fstab);
- if (!result) {
- return Error() << "Could not call fs_mgr_mount_all() " << result.error();
- }
-
- if (*result != 0) {
- return Error() << "fs_mgr_mount_all() failed: " << *result;
+ if (auto result = fs_mgr_umount_all(&fstab); result != 0) {
+ return Error() << "umount_fstab() failed " << result;
}
return {};
}
@@ -689,19 +680,23 @@
return Error() << "Could not read fstab '" << args[1] << "'";
}
- auto result = CallFunctionAndHandleProperties(fs_mgr_swapon_all, fstab);
- if (!result) {
- return Error() << "Could not call fs_mgr_swapon_all() " << result.error();
- }
-
- if (*result == 0) {
- return Error() << "fs_mgr_swapon_all() failed.";
+ if (!fs_mgr_swapon_all(fstab)) {
+ return Error() << "fs_mgr_swapon_all() failed";
}
return {};
}
static Result<void> do_setprop(const BuiltinArguments& args) {
+ if (StartsWith(args[1], "ctl.")) {
+ return Error()
+ << "Cannot set ctl. properties from init; call the Service functions directly";
+ }
+ if (args[1] == kRestoreconProperty) {
+ return Error() << "Cannot set '" << kRestoreconProperty
+ << "' from init; use the restorecon builtin directly";
+ }
+
property_set(args[1], args[2]);
return {};
}
@@ -1017,7 +1012,20 @@
}
static Result<void> do_load_persist_props(const BuiltinArguments& args) {
- load_persist_props();
+ // Devices with FDE have load_persist_props called twice; the first time when the temporary
+ // /data partition is mounted and then again once /data is truly mounted. We do not want to
+ // read persistent properties from the temporary /data partition or mark persistent properties
+ // as having been loaded during the first call, so we return in that case.
+ std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
+ std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
+ if (crypto_state == "encrypted" && crypto_type == "block") {
+ static size_t num_calls = 0;
+ if (++num_calls == 1) return {};
+ }
+
+ SendLoadPersistentPropertiesMessage();
+
+ start_waiting_for_property("ro.persistent_properties.ready", "true");
return {};
}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 1a5ed28..dffd6af 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -602,19 +602,11 @@
return;
}
- // Find the name of the super partition for the GSI. It will either be
- // "userdata", or a block device such as an sdcard. There are no by-name
- // partitions other than userdata that we support installing GSIs to.
+ // Find the super name. PartitionOpener will ensure this translates to the
+ // correct block device path.
auto super = GetMetadataSuperBlockDevice(*metadata.get());
- std::string super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
- std::string super_path;
- if (super_name == "userdata") {
- super_path = "/dev/block/by-name/" + super_name;
- } else {
- super_path = "/dev/block/" + super_name;
- }
-
- if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path)) {
+ auto super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
+ if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_name)) {
LOG(ERROR) << "GSI partition layout could not be instantiated";
return;
}
diff --git a/init/init.cpp b/init/init.cpp
index 18fb0c3..e8eb571 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -61,6 +61,7 @@
#include "mount_handler.h"
#include "mount_namespace.h"
#include "property_service.h"
+#include "proto_utils.h"
#include "reboot.h"
#include "reboot_utils.h"
#include "security.h"
@@ -69,6 +70,7 @@
#include "service.h"
#include "service_parser.h"
#include "sigchld_handler.h"
+#include "system/core/init/property_service.pb.h"
#include "util.h"
using namespace std::chrono_literals;
@@ -90,6 +92,7 @@
static char qemu[32];
static int signal_fd = -1;
+static int property_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
static std::string wait_prop_name;
@@ -613,6 +616,54 @@
selinux_start_time_ns));
}
+void SendLoadPersistentPropertiesMessage() {
+ auto init_message = InitMessage{};
+ init_message.set_load_persistent_properties(true);
+ if (auto result = SendMessage(property_fd, init_message); !result) {
+ LOG(ERROR) << "Failed to send load persistent properties message: " << result.error();
+ }
+}
+
+static void HandlePropertyFd() {
+ auto message = ReadMessage(property_fd);
+ if (!message) {
+ LOG(ERROR) << "Could not read message from property service: " << message.error();
+ return;
+ }
+
+ auto property_message = PropertyMessage{};
+ if (!property_message.ParseFromString(*message)) {
+ LOG(ERROR) << "Could not parse message from property service";
+ return;
+ }
+
+ switch (property_message.msg_case()) {
+ case PropertyMessage::kControlMessage: {
+ auto& control_message = property_message.control_message();
+ bool response = HandleControlMessage(control_message.msg(), control_message.name(),
+ control_message.pid());
+
+ auto init_message = InitMessage{};
+ auto* control_response = init_message.mutable_control_response();
+
+ control_response->set_result(response);
+
+ if (auto result = SendMessage(property_fd, init_message); !result) {
+ LOG(ERROR) << "Failed to send control response: " << result.error();
+ }
+ break;
+ }
+ case PropertyMessage::kChangedMessage: {
+ auto& changed_message = property_message.changed_message();
+ property_changed(changed_message.name(), changed_message.value());
+ break;
+ }
+ default:
+ LOG(ERROR) << "Unknown message type from property service: "
+ << property_message.msg_case();
+ }
+}
+
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@@ -684,7 +735,12 @@
UmountDebugRamdisk();
fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
- StartPropertyService(&epoll);
+
+ StartPropertyService(&property_fd);
+ if (auto result = epoll.RegisterHandler(property_fd, HandlePropertyFd); !result) {
+ LOG(FATAL) << "Could not register epoll handler for property fd: " << result.error();
+ }
+
MountHandler mount_handler(&epoll);
set_usb_controller();
diff --git a/init/init.h b/init/init.h
index cfc28f1..832ce3f 100644
--- a/init/init.h
+++ b/init/init.h
@@ -31,16 +31,14 @@
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
Parser CreateServiceOnlyParser(ServiceList& service_list);
-bool HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
-
-void property_changed(const std::string& name, const std::string& value);
-
bool start_waiting_for_property(const char *name, const char *value);
void DumpState();
void ResetWaitForProp();
+void SendLoadPersistentPropertiesMessage();
+
int SecondStageMain(int argc, char** argv);
} // namespace init
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 17622a3..b7d5743 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,10 +39,10 @@
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
-#include <atomic>
#include <map>
#include <memory>
#include <mutex>
+#include <optional>
#include <queue>
#include <thread>
#include <vector>
@@ -53,7 +53,6 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
#include <property_info_parser/property_info_parser.h>
#include <property_info_serializer/property_info_serializer.h>
#include <selinux/android.h>
@@ -61,11 +60,14 @@
#include <selinux/selinux.h>
#include "debug_ramdisk.h"
+#include "epoll.h"
#include "init.h"
#include "persistent_properties.h"
#include "property_type.h"
+#include "proto_utils.h"
#include "selinux.h"
#include "subcontext.h"
+#include "system/core/init/property_service.pb.h"
#include "util.h"
using namespace std::literals;
@@ -87,14 +89,15 @@
namespace android {
namespace init {
-static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
-
static bool persistent_properties_loaded = false;
static int property_set_fd = -1;
+static int init_socket = -1;
static PropertyInfoAreaFile property_info_area;
+uint32_t HandlePropertySet(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr, std::string* error);
uint32_t InitPropertySet(const std::string& name, const std::string& value);
uint32_t (*property_set)(const std::string& name, const std::string& value) = InitPropertySet;
@@ -166,6 +169,17 @@
return has_access;
}
+static void SendPropertyChanged(const std::string& name, const std::string& value) {
+ auto property_msg = PropertyMessage{};
+ auto* changed_message = property_msg.mutable_changed_message();
+ changed_message->set_name(name);
+ changed_message->set_value(value);
+
+ if (auto result = SendMessage(init_socket, property_msg); !result) {
+ LOG(ERROR) << "Failed to send property changed message: " << result.error();
+ }
+}
+
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
size_t valuelen = value.size();
@@ -201,7 +215,11 @@
if (persistent_properties_loaded && StartsWith(name, "persist.")) {
WritePersistentProperty(name, value);
}
- property_changed(name, value);
+ // If init hasn't started its main loop, then it won't be handling property changed messages
+ // anyway, so there's no need to try to send them.
+ if (init_socket != -1) {
+ SendPropertyChanged(name, value);
+ }
return PROP_SUCCESS;
}
@@ -242,17 +260,6 @@
};
uint32_t InitPropertySet(const std::string& name, const std::string& value) {
- if (StartsWith(name, "ctl.")) {
- LOG(ERROR) << "InitPropertySet: Do not set ctl. properties from init; call the Service "
- "functions directly";
- return PROP_ERROR_INVALID_NAME;
- }
- if (name == kRestoreconProperty) {
- LOG(ERROR) << "InitPropertySet: Do not set '" << kRestoreconProperty
- << "' from init; use the restorecon builtin directly";
- return PROP_ERROR_INVALID_NAME;
- }
-
uint32_t result = 0;
ucred cr = {.pid = 1, .uid = 0, .gid = 0};
std::string error;
@@ -268,8 +275,6 @@
public:
SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {}
- ~SocketConnection() { close(socket_); }
-
bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
return RecvFully(value, sizeof(*value), timeout_ms);
}
@@ -391,12 +396,62 @@
return bytes_left == 0;
}
- int socket_;
+ unique_fd socket_;
ucred cred_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
};
+// Init responds with whether or not the control message was successful. However, init may set
+// properties in the process of handling the control message, particularly when starting services.
+// Therefore we cannot block in SendControlMessage() to wait for init's response. Instead, we store
+// the SocketConnection for the socket that sent the control message. We then return to the main
+// poll loop and handle messages until we get the response from init.
+//
+// Note that this is a queue, since it is possible for more control messages to come while init is
+// handling the first. Both init and property service will handle these in order.
+//
+// Also note that the 1st version of the property service does not expect a result to be sent, so
+// we have a nullopt as a placeholder in the queue to keep track of which control messages have been
+// responded to.
+static std::queue<std::optional<SocketConnection>> pending_control_message_results;
+
+static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
+ std::string* error) {
+ auto property_msg = PropertyMessage{};
+ auto* control_message = property_msg.mutable_control_message();
+ control_message->set_msg(msg);
+ control_message->set_name(name);
+ control_message->set_pid(pid);
+
+ if (auto result = SendMessage(init_socket, property_msg); !result) {
+ *error = "Failed to send control message: " + result.error().message();
+ return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+ }
+
+ return PROP_SUCCESS;
+}
+
+void HandleControlResponse(const InitMessage& init_message) {
+ if (pending_control_message_results.empty()) {
+ LOG(ERROR) << "Got a control response without pending control messages";
+ return;
+ }
+
+ if (!pending_control_message_results.front().has_value()) {
+ pending_control_message_results.pop();
+ return;
+ }
+
+ if (!pending_control_message_results.front().has_value()) {
+ return;
+ }
+
+ auto& control_response = init_message.control_response();
+ uint32_t response =
+ control_response.result() ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+ pending_control_message_results.front()->SendUint32(response);
+ pending_control_message_results.pop();
+}
+
bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr) {
// We check the legacy method first but these properties are dontaudit, so we only log an audit
@@ -470,9 +525,7 @@
}
if (StartsWith(name, "ctl.")) {
- return HandleControlMessage(name.c_str() + 4, value, cr.pid)
- ? PROP_SUCCESS
- : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+ return SendControlMessage(name.c_str() + 4, value, cr.pid, error);
}
// sys.powerctl is a special property that is used to make the device reboot. We want to log
@@ -557,6 +610,10 @@
<< " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
+ if (result == PROP_SUCCESS && StartsWith(prop_name, "ctl.")) {
+ pending_control_message_results.emplace(std::nullopt);
+ }
+
break;
}
@@ -584,7 +641,12 @@
LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
<< " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
- socket.SendUint32(result);
+
+ if (result == PROP_SUCCESS && StartsWith(name, "ctl.")) {
+ pending_control_message_results.emplace(std::move(socket));
+ } else {
+ socket.SendUint32(result);
+ }
break;
}
@@ -743,33 +805,6 @@
}
}
-/* When booting an encrypted system, /data is not mounted when the
- * property service is started, so any properties stored there are
- * not loaded. Vold triggers init to load these properties once it
- * has mounted /data.
- */
-void load_persist_props(void) {
- // Devices with FDE have load_persist_props called twice; the first time when the temporary
- // /data partition is mounted and then again once /data is truly mounted. We do not want to
- // read persistent properties from the temporary /data partition or mark persistent properties
- // as having been loaded during the first call, so we return in that case.
- std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
- std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
- if (crypto_state == "encrypted" && crypto_type == "block") {
- static size_t num_calls = 0;
- if (++num_calls == 1) return;
- }
-
- load_override_properties();
- /* Read persistent properties after all default values have been loaded. */
- auto persistent_properties = LoadPersistentProperties();
- for (const auto& persistent_property_record : persistent_properties.properties()) {
- property_set(persistent_property_record.name(), persistent_property_record.value());
- }
- persistent_properties_loaded = true;
- property_set("ro.persistent_properties.ready", "true");
-}
-
// If the ro.product.[brand|device|manufacturer|model|name] properties have not been explicitly
// set, derive them from ro.product.${partition}.* properties
static void property_initialize_ro_product_props() {
@@ -987,58 +1022,87 @@
selinux_android_restorecon(kPropertyInfosPath, 0);
}
-void StartPropertyService(Epoll* epoll) {
+static void HandleInitSocket() {
+ auto message = ReadMessage(init_socket);
+ if (!message) {
+ LOG(ERROR) << "Could not read message from init_dedicated_recv_socket: " << message.error();
+ return;
+ }
+
+ auto init_message = InitMessage{};
+ if (!init_message.ParseFromString(*message)) {
+ LOG(ERROR) << "Could not parse message from init";
+ return;
+ }
+
+ switch (init_message.msg_case()) {
+ case InitMessage::kControlResponse: {
+ HandleControlResponse(init_message);
+ break;
+ }
+ case InitMessage::kLoadPersistentProperties: {
+ load_override_properties();
+ // Read persistent properties after all default values have been loaded.
+ auto persistent_properties = LoadPersistentProperties();
+ for (const auto& persistent_property_record : persistent_properties.properties()) {
+ InitPropertySet(persistent_property_record.name(),
+ persistent_property_record.value());
+ }
+ InitPropertySet("ro.persistent_properties.ready", "true");
+ persistent_properties_loaded = true;
+ break;
+ }
+ default:
+ LOG(ERROR) << "Unknown message type from init: " << init_message.msg_case();
+ }
+}
+
+static void PropertyServiceThread() {
+ Epoll epoll;
+ if (auto result = epoll.Open(); !result) {
+ LOG(FATAL) << result.error();
+ }
+
+ if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
+ LOG(FATAL) << result.error();
+ }
+
+ if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result) {
+ LOG(FATAL) << result.error();
+ }
+
+ while (true) {
+ if (auto result = epoll.Wait(std::nullopt); !result) {
+ LOG(ERROR) << result.error();
+ }
+ }
+}
+
+void StartPropertyService(int* epoll_socket) {
property_set("ro.property_service.version", "2");
- if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- false, 0666, 0, 0, {})) {
+ int sockets[2];
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
+ PLOG(FATAL) << "Failed to socketpair() between property_service and init";
+ }
+ *epoll_socket = sockets[0];
+ init_socket = sockets[1];
+
+ if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC, false, 0666, 0, 0,
+ {})) {
property_set_fd = *result;
} else {
- PLOG(FATAL) << "start_property_service socket creation failed: " << result.error();
+ LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
}
listen(property_set_fd, 8);
- if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
- PLOG(FATAL) << result.error();
- }
-}
+ std::thread{PropertyServiceThread}.detach();
-Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f) {
- unique_fd reader;
- unique_fd writer;
- if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &reader, &writer)) {
- return ErrnoError() << "Could not create socket pair";
- }
-
- int result = 0;
- std::atomic<bool> end = false;
- auto thread = std::thread{[&f, &result, &end, &writer] {
- result = f();
- end = true;
- send(writer, "1", 1, 0);
- }};
-
- Epoll epoll;
- if (auto result = epoll.Open(); !result) {
- return Error() << "Could not create epoll: " << result.error();
- }
- if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
- return Error() << "Could not register epoll handler for property fd: " << result.error();
- }
-
- // No-op function, just used to break from loop.
- if (auto result = epoll.RegisterHandler(reader, [] {}); !result) {
- return Error() << "Could not register epoll handler for ending thread:" << result.error();
- }
-
- while (!end) {
- epoll.Wait({});
- }
-
- thread.join();
-
- return result;
+ property_set = [](const std::string& key, const std::string& value) -> uint32_t {
+ android::base::SetProperty(key, value);
+ return 0;
+ };
}
} // namespace init
diff --git a/init/property_service.h b/init/property_service.h
index dc47b4d..8f7d8d9 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,34 +18,22 @@
#include <sys/socket.h>
-#include <functional>
#include <string>
#include "epoll.h"
-#include "result.h"
namespace android {
namespace init {
+static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
+
bool CanReadProperty(const std::string& source_context, const std::string& name);
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
-uint32_t HandlePropertySet(const std::string& name, const std::string& value,
- const std::string& source_context, const ucred& cr, std::string* error);
-
void property_init();
void property_load_boot_defaults(bool load_debug_prop);
-void load_persist_props();
-void StartPropertyService(Epoll* epoll);
-
-template <typename F, typename... Args>
-Result<int> CallFunctionAndHandleProperties(F&& f, Args&&... args) {
- Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f);
-
- auto func = [&] { return f(args...); };
- return CallFunctionAndHandlePropertiesImpl(func);
-}
+void StartPropertyService(int* epoll_socket);
} // namespace init
} // namespace android
diff --git a/init/property_service.proto b/init/property_service.proto
new file mode 100644
index 0000000..894fa13
--- /dev/null
+++ b/init/property_service.proto
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+message PropertyMessage {
+ message ControlMessage {
+ optional string msg = 1;
+ optional string name = 2;
+ optional int32 pid = 3;
+ }
+
+ message ChangedMessage {
+ optional string name = 1;
+ optional string value = 2;
+ }
+
+ oneof msg {
+ ControlMessage control_message = 1;
+ ChangedMessage changed_message = 2;
+ };
+}
+
+message InitMessage {
+ message ControlResponse { optional bool result = 1; }
+
+ oneof msg {
+ ControlResponse control_response = 1;
+ bool load_persistent_properties = 2;
+ };
+}
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index c038aff..0f4cd0d 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -56,6 +56,11 @@
}
TEST(property_service, non_utf8_value) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+
ASSERT_TRUE(SetProperty("property_service_utf8_test", "base_success"));
EXPECT_FALSE(SetProperty("property_service_utf8_test", "\x80"));
EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xC2\x01"));
diff --git a/init/proto_utils.h b/init/proto_utils.h
new file mode 100644
index 0000000..93a7d57
--- /dev/null
+++ b/init/proto_utils.h
@@ -0,0 +1,62 @@
+/*
+ * 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 <sys/socket.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+constexpr size_t kBufferSize = 4096;
+
+inline Result<std::string> ReadMessage(int socket) {
+ char buffer[kBufferSize] = {};
+ auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
+ if (result == 0) {
+ return Error();
+ } else if (result < 0) {
+ return ErrnoError();
+ }
+ return std::string(buffer, result);
+}
+
+template <typename T>
+Result<void> SendMessage(int socket, const T& message) {
+ std::string message_string;
+ if (!message.SerializeToString(&message_string)) {
+ return Error() << "Unable to serialize message";
+ }
+
+ if (message_string.size() > kBufferSize) {
+ return Error() << "Serialized message too long to send";
+ }
+
+ if (auto result =
+ TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));
+ result != static_cast<long>(message_string.size())) {
+ return ErrnoError() << "send() failed to send message contents";
+ }
+ return {};
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 00f91d8..ec93b58 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -18,16 +18,17 @@
#include <fcntl.h>
#include <poll.h>
-#include <sys/socket.h>
#include <unistd.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <selinux/android.h>
#include "action.h"
#include "builtins.h"
+#include "proto_utils.h"
#include "util.h"
#if defined(__ANDROID__)
@@ -59,45 +60,6 @@
namespace {
-constexpr size_t kBufferSize = 4096;
-
-Result<std::string> ReadMessage(int socket) {
- char buffer[kBufferSize] = {};
- auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
- if (result == 0) {
- return Error();
- } else if (result < 0) {
- return ErrnoError();
- }
- return std::string(buffer, result);
-}
-
-template <typename T>
-Result<void> SendMessage(int socket, const T& message) {
- std::string message_string;
- if (!message.SerializeToString(&message_string)) {
- return Error() << "Unable to serialize message";
- }
-
- if (message_string.size() > kBufferSize) {
- return Error() << "Serialized message too long to send";
- }
-
- if (auto result =
- TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));
- result != static_cast<long>(message_string.size())) {
- return ErrnoError() << "send() failed to send message contents";
- }
- return {};
-}
-
-std::vector<std::pair<std::string, std::string>> properties_to_set;
-
-uint32_t SubcontextPropertySet(const std::string& name, const std::string& value) {
- properties_to_set.emplace_back(name, value);
- return 0;
-}
-
class SubcontextProcess {
public:
SubcontextProcess(const BuiltinFunctionMap* function_map, std::string context, int init_fd)
@@ -131,14 +93,6 @@
result = RunBuiltinFunction(map_result->function, args, context_);
}
- for (const auto& [name, value] : properties_to_set) {
- auto property = reply->add_properties_to_set();
- property->set_name(name);
- property->set_value(value);
- }
-
- properties_to_set.clear();
-
if (result) {
reply->set_success(true);
} else {
@@ -224,7 +178,10 @@
SelabelInitialize();
- property_set = SubcontextPropertySet;
+ property_set = [](const std::string& key, const std::string& value) -> uint32_t {
+ android::base::SetProperty(key, value);
+ return 0;
+ };
auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
subcontext_process.MainLoop();
@@ -311,15 +268,6 @@
return subcontext_reply.error();
}
- for (const auto& property : subcontext_reply->properties_to_set()) {
- ucred cr = {.pid = pid_, .uid = 0, .gid = 0};
- std::string error;
- if (HandlePropertySet(property.name(), property.value(), context_, cr, &error) != 0) {
- LOG(ERROR) << "Subcontext init could not set '" << property.name() << "' to '"
- << property.value() << "': " << error;
- }
- }
-
if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
auto& failure = subcontext_reply->failure();
return ResultError(failure.error_string(), failure.error_errno());
diff --git a/init/subcontext.proto b/init/subcontext.proto
index c31f4fb..e68115e 100644
--- a/init/subcontext.proto
+++ b/init/subcontext.proto
@@ -38,10 +38,4 @@
Failure failure = 2;
ExpandArgsReply expand_args_reply = 3;
}
-
- message PropertyToSet {
- optional string name = 1;
- optional string value = 2;
- }
- repeated PropertyToSet properties_to_set = 4;
}
\ No newline at end of file
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index ae89c38..dcbff82 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -43,7 +43,7 @@
template <typename F>
void RunTest(F&& test_function) {
if (getuid() != 0) {
- GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ GTEST_SKIP() << "Skipping test, must be run as root.";
return;
}
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index bfdc28e..2d7d2f8 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -68,7 +68,7 @@
TEST(ueventd, setegid_IsPerThread) {
if (getuid() != 0) {
- GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ GTEST_SKIP() << "Skipping test, must be run as root.";
return;
}
@@ -92,11 +92,11 @@
TEST(ueventd, setfscreatecon_IsPerThread) {
if (getuid() != 0) {
- GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ GTEST_SKIP() << "Skipping test, must be run as root.";
return;
}
if (!is_selinux_enabled() || security_getenforce() == 1) {
- GTEST_LOG_(INFO) << "Skipping test, SELinux must be enabled and in permissive mode.";
+ GTEST_SKIP() << "Skipping test, SELinux must be enabled and in permissive mode.";
return;
}
@@ -127,7 +127,7 @@
TEST(ueventd, selabel_lookup_MultiThreaded) {
if (getuid() != 0) {
- GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ GTEST_SKIP() << "Skipping test, must be run as root.";
return;
}
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index 7f5768c..9a33b55 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -124,7 +124,7 @@
// we might as well end up loading them from /system/lib or /product/lib
// For now we rely on CTS test to catch things like this but
// it should probably be addressed in the future.
- for (const auto& soname : android::base::Split(default_public_libraries(), ":")) {
+ for (const auto& soname : android::base::Split(preloadable_public_libraries(), ":")) {
LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
"Error preloading public library %s: %s", soname.c_str(), dlerror());
}
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
index a641109..75255b6 100644
--- a/libnativeloader/native_loader_test.cpp
+++ b/libnativeloader/native_loader_test.cpp
@@ -29,6 +29,7 @@
#include "public_libraries.h"
using namespace ::testing;
+using namespace ::android::nativeloader::internal;
namespace android {
namespace nativeloader {
@@ -289,7 +290,7 @@
void SetExpectations() {
std::vector<std::string> default_public_libs =
- android::base::Split(default_public_libraries(), ":");
+ android::base::Split(preloadable_public_libraries(), ":");
for (auto l : default_public_libs) {
EXPECT_CALL(*mock, dlopen(StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE))
.WillOnce(Return(any_nonnull));
@@ -576,5 +577,87 @@
INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool());
+const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
+ [](const struct ConfigEntry&) -> Result<bool> { return true; };
+
+TEST(NativeLoaderConfigParser, NamesAndComments) {
+ const char file_content[] = R"(
+######
+
+libA.so
+#libB.so
+
+
+ libC.so
+libD.so
+ #### libE.so
+)";
+ const std::vector<std::string> expected_result = {"libA.so", "libC.so", "libD.so"};
+ Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithBitness) {
+ const char file_content[] = R"(
+libA.so 32
+libB.so 64
+libC.so
+)";
+#if defined(__LP64__)
+ const std::vector<std::string> expected_result = {"libB.so", "libC.so"};
+#else
+ const std::vector<std::string> expected_result = {"libA.so", "libC.so"};
+#endif
+ Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithNoPreload) {
+ const char file_content[] = R"(
+libA.so nopreload
+libB.so nopreload
+libC.so
+)";
+
+ const std::vector<std::string> expected_result = {"libC.so"};
+ Result<std::vector<std::string>> result =
+ ParseConfig(file_content,
+ [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithNoPreloadAndBitness) {
+ const char file_content[] = R"(
+libA.so nopreload 32
+libB.so 64 nopreload
+libC.so 32
+libD.so 64
+libE.so nopreload
+)";
+
+#if defined(__LP64__)
+ const std::vector<std::string> expected_result = {"libD.so"};
+#else
+ const std::vector<std::string> expected_result = {"libC.so"};
+#endif
+ Result<std::vector<std::string>> result =
+ ParseConfig(file_content,
+ [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, RejectMalformed) {
+ ASSERT_FALSE(ParseConfig("libA.so 32 64", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so 32 32", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so 32 nopreload 64", always_true));
+ ASSERT_FALSE(ParseConfig("32 libA.so nopreload", always_true));
+ ASSERT_FALSE(ParseConfig("nopreload libA.so 32", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so nopreload # comment", always_true));
+}
+
} // namespace nativeloader
} // namespace android
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
index 6cee668..3694360 100644
--- a/libnativeloader/public_libraries.cpp
+++ b/libnativeloader/public_libraries.cpp
@@ -34,7 +34,8 @@
namespace android::nativeloader {
-using namespace std::string_literals;
+using namespace internal;
+using namespace ::std::string_literals;
using android::base::ErrnoError;
using android::base::Errorf;
using android::base::Result;
@@ -95,53 +96,21 @@
file_name->insert(insert_pos, vndk_version_str());
}
-const std::function<Result<void>(const std::string&)> always_true =
- [](const std::string&) -> Result<void> { return {}; };
+const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
+ [](const struct ConfigEntry&) -> Result<bool> { return true; };
Result<std::vector<std::string>> ReadConfig(
const std::string& configFile,
- const std::function<Result<void>(const std::string& /* soname */)>& check_soname) {
- // Read list of public native libraries from the config file.
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
std::string file_content;
if (!base::ReadFileToString(configFile, &file_content)) {
return ErrnoError();
}
-
- std::vector<std::string> lines = base::Split(file_content, "\n");
-
- std::vector<std::string> sonames;
- for (auto& line : lines) {
- auto trimmed_line = base::Trim(line);
- if (trimmed_line[0] == '#' || trimmed_line.empty()) {
- continue;
- }
- size_t space_pos = trimmed_line.rfind(' ');
- if (space_pos != std::string::npos) {
- std::string type = trimmed_line.substr(space_pos + 1);
- if (type != "32" && type != "64") {
- return Errorf("Malformed line: {}", line);
- }
-#if defined(__LP64__)
- // Skip 32 bit public library.
- if (type == "32") {
- continue;
- }
-#else
- // Skip 64 bit public library.
- if (type == "64") {
- continue;
- }
-#endif
- trimmed_line.resize(space_pos);
- }
-
- auto ret = check_soname(trimmed_line);
- if (!ret) {
- return ret.error();
- }
- sonames.push_back(trimmed_line);
+ Result<std::vector<std::string>> result = ParseConfig(file_content, filter_fn);
+ if (!result) {
+ return Errorf("Cannot parse {}: {}", configFile, result.error().message());
}
- return sonames;
+ return result;
}
void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
@@ -165,13 +134,13 @@
config_file_path.c_str());
auto ret = ReadConfig(
- config_file_path, [&company_name](const std::string& soname) -> Result<void> {
- if (android::base::StartsWith(soname, "lib") &&
- android::base::EndsWith(soname, "." + company_name + ".so")) {
- return {};
+ config_file_path, [&company_name](const struct ConfigEntry& entry) -> Result<bool> {
+ if (android::base::StartsWith(entry.soname, "lib") &&
+ android::base::EndsWith(entry.soname, "." + company_name + ".so")) {
+ return true;
} else {
- return Errorf("Library name \"{}\" does not end with the company name {}.", soname,
- company_name);
+ return Errorf("Library name \"{}\" does not end with the company name {}.",
+ entry.soname, company_name);
}
});
if (ret) {
@@ -185,9 +154,16 @@
}
}
-static std::string InitDefaultPublicLibraries() {
+static std::string InitDefaultPublicLibraries(bool for_preload) {
std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
- auto sonames = ReadConfig(config_file, always_true);
+ auto sonames =
+ ReadConfig(config_file, [&for_preload](const struct ConfigEntry& entry) -> Result<bool> {
+ if (for_preload) {
+ return !entry.nopreload;
+ } else {
+ return true;
+ }
+ });
if (!sonames) {
LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
config_file.c_str(), sonames.error().message().c_str());
@@ -290,8 +266,13 @@
} // namespace
+const std::string& preloadable_public_libraries() {
+ static std::string list = InitDefaultPublicLibraries(/*for_preload*/ true);
+ return list;
+}
+
const std::string& default_public_libraries() {
- static std::string list = InitDefaultPublicLibraries();
+ static std::string list = InitDefaultPublicLibraries(/*for_preload*/ false);
return list;
}
@@ -325,4 +306,61 @@
return list;
}
+namespace internal {
+// Exported for testing
+Result<std::vector<std::string>> ParseConfig(
+ const std::string& file_content,
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
+ std::vector<std::string> lines = base::Split(file_content, "\n");
+
+ std::vector<std::string> sonames;
+ for (auto& line : lines) {
+ auto trimmed_line = base::Trim(line);
+ if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+ continue;
+ }
+
+ std::vector<std::string> tokens = android::base::Split(trimmed_line, " ");
+ if (tokens.size() < 1 || tokens.size() > 3) {
+ return Errorf("Malformed line \"{}\"", line);
+ }
+ struct ConfigEntry entry = {.soname = "", .nopreload = false, .bitness = ALL};
+ size_t i = tokens.size();
+ while (i-- > 0) {
+ if (tokens[i] == "nopreload") {
+ entry.nopreload = true;
+ } else if (tokens[i] == "32" || tokens[i] == "64") {
+ if (entry.bitness != ALL) {
+ return Errorf("Malformed line \"{}\": bitness can be specified only once", line);
+ }
+ entry.bitness = tokens[i] == "32" ? ONLY_32 : ONLY_64;
+ } else {
+ if (i != 0) {
+ return Errorf("Malformed line \"{}\"", line);
+ }
+ entry.soname = tokens[i];
+ }
+ }
+
+ // skip 32-bit lib on 64-bit process and vice versa
+#if defined(__LP64__)
+ if (entry.bitness == ONLY_32) continue;
+#else
+ if (entry.bitness == ONLY_64) continue;
+#endif
+
+ Result<bool> ret = filter_fn(entry);
+ if (!ret) {
+ return ret.error();
+ }
+ if (*ret) {
+ // filter_fn has returned true.
+ sonames.push_back(entry.soname);
+ }
+ }
+ return sonames;
+}
+
+} // namespace internal
+
} // namespace android::nativeloader
diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h
index 9bb3366..2de4172 100644
--- a/libnativeloader/public_libraries.h
+++ b/libnativeloader/public_libraries.h
@@ -15,13 +15,19 @@
*/
#pragma once
+#include <algorithm>
#include <string>
+#include <android-base/result.h>
+
namespace android::nativeloader {
+using android::base::Result;
+
// These provide the list of libraries that are available to the namespace for apps.
// Not all of the libraries are available to apps. Depending on the context,
// e.g., if it is a vendor app or not, different set of libraries are made available.
+const std::string& preloadable_public_libraries();
const std::string& default_public_libraries();
const std::string& runtime_public_libraries();
const std::string& vendor_public_libraries();
@@ -30,4 +36,21 @@
const std::string& llndk_libraries();
const std::string& vndksp_libraries();
-}; // namespace android::nativeloader
+// These are exported for testing
+namespace internal {
+
+enum Bitness { ALL = 0, ONLY_32, ONLY_64 };
+
+struct ConfigEntry {
+ std::string soname;
+ bool nopreload;
+ Bitness bitness;
+};
+
+Result<std::vector<std::string>> ParseConfig(
+ const std::string& file_content,
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn);
+
+} // namespace internal
+
+} // namespace android::nativeloader
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 4d34b67..9b0075d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -127,6 +127,10 @@
mkdir /mnt/expand 0771 system system
mkdir /mnt/appfuse 0711 root root
+ # tmpfs place for BORINGSSL_self_test() to remember whether it has run
+ mkdir /dev/boringssl 0755 root root
+ mkdir /dev/boringssl/selftest 0755 root root
+
# Storage views to support runtime permissions
mkdir /mnt/runtime 0700 root root
mkdir /mnt/runtime/default 0755 root root