Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c88d8f93 authored by Tom Cherry's avatar Tom Cherry
Browse files

init: Replace property_set() with android::base::SetProperty()

Init is no longer a special case and talks to property service just
like every other client, therefore move it away from property_set()
and to android::base::SetProperty().

In doing so, this change moves the initial property set up from the
kernel command line and property files directly into PropertyInit().
This makes the responsibilities between init and property services
more clear.

Test: boot, unit test cases
Change-Id: I36b8c83e845d887f1b203355c2391ec123c3d05f
parent f7a6c458
Loading
Loading
Loading
Loading
+16 −15
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@
using namespace std::literals::string_literals;

using android::base::Basename;
using android::base::SetProperty;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -561,16 +562,16 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
            LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?";
            trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
        }
        property_set("ro.crypto.state", "encrypted");
        property_set("ro.crypto.type", "block");
        SetProperty("ro.crypto.state", "encrypted");
        SetProperty("ro.crypto.type", "block");
        ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
        return {};
    } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
        property_set("ro.crypto.state", "unencrypted");
        SetProperty("ro.crypto.state", "unencrypted");
        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
        return {};
    } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
        property_set("ro.crypto.state", "unsupported");
        SetProperty("ro.crypto.state", "unsupported");
        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
        return {};
    } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
@@ -586,8 +587,8 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
        if (!FscryptInstallKeyring()) {
            return Error() << "FscryptInstallKeyring() failed";
        }
        property_set("ro.crypto.state", "encrypted");
        property_set("ro.crypto.type", "file");
        SetProperty("ro.crypto.state", "encrypted");
        SetProperty("ro.crypto.type", "file");

        // Although encrypted, we have device key, so we do not need to
        // do anything different from the nonencrypted case.
@@ -597,8 +598,8 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
        if (!FscryptInstallKeyring()) {
            return Error() << "FscryptInstallKeyring() failed";
        }
        property_set("ro.crypto.state", "encrypted");
        property_set("ro.crypto.type", "file");
        SetProperty("ro.crypto.state", "encrypted");
        SetProperty("ro.crypto.type", "file");

        // Although encrypted, vold has already set the device up, so we do not need to
        // do anything different from the nonencrypted case.
@@ -608,8 +609,8 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
        if (!FscryptInstallKeyring()) {
            return Error() << "FscryptInstallKeyring() failed";
        }
        property_set("ro.crypto.state", "encrypted");
        property_set("ro.crypto.type", "file");
        SetProperty("ro.crypto.state", "encrypted");
        SetProperty("ro.crypto.type", "file");

        // Although encrypted, vold has already set the device up, so we do not need to
        // do anything different from the nonencrypted case.
@@ -662,7 +663,7 @@ static Result<void> do_mount_all(const BuiltinArguments& args) {
    }

    auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
    property_set(prop_name, std::to_string(t.duration().count()));
    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 */
@@ -718,7 +719,7 @@ static Result<void> do_setprop(const BuiltinArguments& args) {
                       << "' from init; use the restorecon builtin directly";
    }

    property_set(args[1], args[2]);
    SetProperty(args[1], args[2]);
    return {};
}

@@ -832,7 +833,7 @@ static Result<void> do_verity_update_state(const BuiltinArguments& args) {
        // To be consistent in vboot 1.0 and vboot 2.0 (AVB), use "system" for the partition even
        // for system as root, so it has property [partition.system.verified].
        std::string partition = entry.mount_point == "/" ? "system" : Basename(entry.mount_point);
        property_set("partition." + partition + ".verified", std::to_string(mode));
        SetProperty("partition." + partition + ".verified", std::to_string(mode));
    }

    return {};
@@ -1221,7 +1222,7 @@ static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
static Result<void> do_finish_userspace_reboot(const BuiltinArguments&) {
    LOG(INFO) << "Userspace reboot successfully finished";
    boot_clock::time_point now = boot_clock::now();
    property_set("sys.init.userspace_reboot.last_finished",
    SetProperty("sys.init.userspace_reboot.last_finished",
                std::to_string(now.time_since_epoch().count()));
    if (!android::sysprop::InitProperties::userspace_reboot_in_progress(false)) {
        return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
+0 −9
Original line number Diff line number Diff line
@@ -39,15 +39,6 @@ namespace init {
inline bool CanReadProperty(const std::string&, const std::string&) {
    return true;
}
inline uint32_t SetProperty(const std::string& key, const std::string& value) {
    android::base::SetProperty(key, value);
    return 0;
}
inline uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
inline uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&,
                                  const ucred&, std::string*) {
    return 0;
}

// reboot_utils.h
inline void SetFatalRebootTarget() {}
+40 −113
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ using namespace std::string_literals;
using android::base::boot_clock;
using android::base::GetProperty;
using android::base::ReadFileToString;
using android::base::SetProperty;
using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
@@ -91,8 +92,6 @@ namespace init {

static int property_triggers_enabled = 0;

static char qemu[32];

static int signal_fd = -1;
static int property_fd = -1;

@@ -101,7 +100,6 @@ static std::string wait_prop_name;
static std::string wait_prop_value;
static std::string shutdown_command;
static bool do_shutdown = false;
static bool load_debug_prop = false;

static std::unique_ptr<Subcontext> subcontext;

@@ -208,7 +206,7 @@ void property_changed(const std::string& name, const std::string& value) {
    // to wait.
    if (name == kColdBootDoneProp) {
        auto time_waited = waiting_for_prop ? waiting_for_prop->duration().count() : 0;
        property_set("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
        SetProperty("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
    }

    if (waiting_for_prop) {
@@ -364,87 +362,17 @@ static Result<void> SetupCgroupsAction(const BuiltinArguments&) {
    return {};
}

static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
    if (key.empty()) return;

    if (for_emulator) {
        // In the emulator, export any kernel option with the "ro.kernel." prefix.
        property_set("ro.kernel." + key, value);
        return;
    }

    if (key == "qemu") {
        strlcpy(qemu, value.c_str(), sizeof(qemu));
    } else if (android::base::StartsWith(key, "androidboot.")) {
        property_set("ro.boot." + key.substr(12), value);
    }
}

static void export_oem_lock_status() {
    if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
        return;
    }
    import_kernel_cmdline(
            false, [](const std::string& key, const std::string& value, bool in_qemu) {
    ImportKernelCmdline([](const std::string& key, const std::string& value) {
        if (key == "androidboot.verifiedbootstate") {
                    property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
            SetProperty("ro.boot.flash.locked", value == "orange" ? "0" : "1");
        }
    });
}

static void export_kernel_boot_props() {
    constexpr const char* UNSET = "";
    struct {
        const char *src_prop;
        const char *dst_prop;
        const char *default_value;
    } prop_map[] = {
        { "ro.boot.serialno",   "ro.serialno",   UNSET, },
        { "ro.boot.mode",       "ro.bootmode",   "unknown", },
        { "ro.boot.baseband",   "ro.baseband",   "unknown", },
        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
        { "ro.boot.hardware",   "ro.hardware",   "unknown", },
        { "ro.boot.revision",   "ro.revision",   "0", },
    };
    for (const auto& prop : prop_map) {
        std::string value = GetProperty(prop.src_prop, prop.default_value);
        if (value != UNSET)
            property_set(prop.dst_prop, value);
    }
}

static void process_kernel_dt() {
    if (!is_android_dt_value_expected("compatible", "android,firmware")) {
        return;
    }

    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
    if (!dir) return;

    std::string dt_file;
    struct dirent *dp;
    while ((dp = readdir(dir.get())) != NULL) {
        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
            continue;
        }

        std::string file_name = get_android_dt_dir() + dp->d_name;

        android::base::ReadFileToString(file_name, &dt_file);
        std::replace(dt_file.begin(), dt_file.end(), ',', '.');

        property_set("ro.boot."s + dp->d_name, dt_file);
    }
}

static void process_kernel_cmdline() {
    // The first pass does the common stuff, and finds if we are in qemu.
    // The second pass is only necessary for qemu to export all kernel params
    // as properties.
    import_kernel_cmdline(false, import_kernel_nv);
    if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}

static Result<void> property_enable_triggers_action(const BuiltinArguments& args) {
    /* Enable property triggers. */
    property_triggers_enabled = 1;
@@ -468,7 +396,7 @@ static void set_usb_controller() {
    while ((dp = readdir(dir.get())) != nullptr) {
        if (dp->d_name[0] == '.') continue;

        property_set("sys.usb.controller", dp->d_name);
        SetProperty("sys.usb.controller", dp->d_name);
        break;
    }
}
@@ -591,7 +519,7 @@ static void RecordStageBoottimes(const boot_clock::time_point& second_stage_star
    int64_t first_stage_start_time_ns = -1;
    if (auto first_stage_start_time_str = getenv(kEnvFirstStageStartedAt);
        first_stage_start_time_str) {
        property_set("ro.boottime.init", first_stage_start_time_str);
        SetProperty("ro.boottime.init", first_stage_start_time_str);
        android::base::ParseInt(first_stage_start_time_str, &first_stage_start_time_ns);
    }
    unsetenv(kEnvFirstStageStartedAt);
@@ -605,9 +533,9 @@ static void RecordStageBoottimes(const boot_clock::time_point& second_stage_star
    if (selinux_start_time_ns == -1) return;
    if (first_stage_start_time_ns == -1) return;

    property_set("ro.boottime.init.first_stage",
    SetProperty("ro.boottime.init.first_stage",
                std::to_string(selinux_start_time_ns - first_stage_start_time_ns));
    property_set("ro.boottime.init.selinux",
    SetProperty("ro.boottime.init.selinux",
                std::to_string(second_stage_start_time.time_since_epoch().count() -
                               selinux_start_time_ns));
}
@@ -706,34 +634,27 @@ int SecondStageMain(int argc, char** argv) {
    // Indicate that booting is in progress to background fw loaders, etc.
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

    property_init();

    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    process_kernel_dt();
    process_kernel_cmdline();

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    export_kernel_boot_props();

    // Make the time that init stages started available for bootstat to log.
    RecordStageBoottimes(start_time);

    // Set libavb version for Framework-only OTA match in Treble build.
    const char* avb_version = getenv("INIT_AVB_VERSION");
    if (avb_version) property_set("ro.boot.avb_version", avb_version);

    // See if need to load debug props to allow adb root, when the device is unlocked.
    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
    bool load_debug_prop = false;
    if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
        load_debug_prop = "true"s == force_debuggable_env;
    }

    // Clean up our environment.
    unsetenv("INIT_AVB_VERSION");
    unsetenv("INIT_FORCE_DEBUGGABLE");

    // Umount the debug ramdisk so property service doesn't read .prop files from there, when it
    // is not meant to.
    if (!load_debug_prop) {
        UmountDebugRamdisk();
    }

    PropertyInit();

    // Umount the debug ramdisk after property service has read the .prop files when it means to.
    if (load_debug_prop) {
        UmountDebugRamdisk();
    }

    // Now set up SELinux for second stage.
    SelinuxSetupKernelLogging();
    SelabelInitialize();
@@ -746,16 +667,22 @@ int SecondStageMain(int argc, char** argv) {

    InstallSignalFdHandler(&epoll);

    property_load_boot_defaults(load_debug_prop);
    UmountDebugRamdisk();
    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();

    StartPropertyService(&property_fd);
    if (auto result = epoll.RegisterHandler(property_fd, HandlePropertyFd); !result) {
        LOG(FATAL) << "Could not register epoll handler for property fd: " << result.error();
    }

    // Make the time that init stages started available for bootstat to log.
    RecordStageBoottimes(start_time);

    // Set libavb version for Framework-only OTA match in Treble build.
    if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) {
        SetProperty("ro.boot.avb_version", avb_version);
    }
    unsetenv("INIT_AVB_VERSION");

    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();
    MountHandler mount_handler(&epoll);
    set_usb_controller();

@@ -779,9 +706,9 @@ int SecondStageMain(int argc, char** argv) {

    // Make the GSI status available before scripts start running.
    if (android::gsi::IsGsiRunning()) {
        property_set("ro.gsid.image_running", "1");
        SetProperty("ro.gsid.image_running", "1");
    } else {
        property_set("ro.gsid.image_running", "0");
        SetProperty("ro.gsid.image_running", "0");
    }

    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
+1 −2
Original line number Diff line number Diff line
@@ -38,7 +38,6 @@
#include <libdm/dm.h>

#include "epoll.h"
#include "property_service.h"

namespace android {
namespace init {
@@ -96,7 +95,7 @@ void SetMountProperty(const MountHandlerEntry& entry, bool add) {
    // handling, except for clearing non-existent or already clear property.
    // Goal is reduction of empty properties and associated triggers.
    if (value.empty() && android::base::GetProperty(mount_prop, "").empty()) return;
    property_set(mount_prop, value);
    android::base::SetProperty(mount_prop, value);
}

}  // namespace
+108 −41
Original line number Diff line number Diff line
@@ -97,8 +97,6 @@ static bool accept_messages = false;

static PropertyInfoAreaFile property_info_area;

void CreateSerializedPropertyInfo();

struct PropertyAuditData {
    const ucred* cr;
    const char* name;
@@ -117,21 +115,6 @@ static int PropertyAuditCallback(void* data, security_class_t /*cls*/, char* buf
    return 0;
}

void property_init() {
    selinux_callback cb;
    cb.func_audit = PropertyAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
        LOG(FATAL) << "Failed to load serialized property info file";
    }
}

bool CanReadProperty(const std::string& source_context, const std::string& name) {
    const char* target_context = nullptr;
    property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
@@ -527,20 +510,6 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value,
    return PropertySet(name, value, error);
}

uint32_t InitPropertySet(const std::string& name, const std::string& value) {
    uint32_t result = 0;
    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
    std::string error;
    result = HandlePropertySet(name, value, kInitContext, cr, nullptr, &error);
    if (result != PROP_SUCCESS) {
        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
    }

    return result;
}

uint32_t (*property_set)(const std::string& name, const std::string& value) = InitPropertySet;

static void handle_property_set_fd() {
    static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */

@@ -634,6 +603,18 @@ static void handle_property_set_fd() {
    }
}

uint32_t InitPropertySet(const std::string& name, const std::string& value) {
    uint32_t result = 0;
    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
    std::string error;
    result = HandlePropertySet(name, value, kInitContext, cr, nullptr, &error);
    if (result != PROP_SUCCESS) {
        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
    }

    return result;
}

static bool load_properties_from_file(const char*, const char*,
                                      std::map<std::string, std::string>*);

@@ -765,11 +746,11 @@ static void update_sys_usb_config() {
    bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
    std::string config = android::base::GetProperty("persist.sys.usb.config", "");
    if (config.empty()) {
        property_set("persist.sys.usb.config", is_debuggable ? "adb" : "none");
        InitPropertySet("persist.sys.usb.config", is_debuggable ? "adb" : "none");
    } else if (is_debuggable && config.find("adb") == std::string::npos &&
               config.length() + 4 < PROP_VALUE_MAX) {
        config.append(",adb");
        property_set("persist.sys.usb.config", config);
        InitPropertySet("persist.sys.usb.config", config);
    }
}

@@ -890,7 +871,7 @@ static void property_derive_build_fingerprint() {
    }
}

void property_load_boot_defaults(bool load_debug_prop) {
void PropertyLoadBootDefaults() {
    // TODO(b/117892318): merge prop.default and build.prop files into one
    // We read the properties and their values into a map, in order to always allow properties
    // loaded in the later property files to override the properties in loaded in the earlier
@@ -916,7 +897,7 @@ void property_load_boot_defaults(bool load_debug_prop) {
    load_properties_from_file("/product/build.prop", nullptr, &properties);
    load_properties_from_file("/factory/factory.prop", "ro.*", &properties);

    if (load_debug_prop) {
    if (access(kDebugRamdiskProp, R_OK) == 0) {
        LOG(INFO) << "Loading " << kDebugRamdiskProp;
        load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
    }
@@ -1009,6 +990,97 @@ void CreateSerializedPropertyInfo() {
    selinux_android_restorecon(kPropertyInfosPath, 0);
}

static void ExportKernelBootProps() {
    constexpr const char* UNSET = "";
    struct {
        const char* src_prop;
        const char* dst_prop;
        const char* default_value;
    } prop_map[] = {
            // clang-format off
        { "ro.boot.serialno",   "ro.serialno",   UNSET, },
        { "ro.boot.mode",       "ro.bootmode",   "unknown", },
        { "ro.boot.baseband",   "ro.baseband",   "unknown", },
        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
        { "ro.boot.hardware",   "ro.hardware",   "unknown", },
        { "ro.boot.revision",   "ro.revision",   "0", },
            // clang-format on
    };
    for (const auto& prop : prop_map) {
        std::string value = GetProperty(prop.src_prop, prop.default_value);
        if (value != UNSET) InitPropertySet(prop.dst_prop, value);
    }
}

static void ProcessKernelDt() {
    if (!is_android_dt_value_expected("compatible", "android,firmware")) {
        return;
    }

    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
    if (!dir) return;

    std::string dt_file;
    struct dirent* dp;
    while ((dp = readdir(dir.get())) != NULL) {
        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") ||
            !strcmp(dp->d_name, "name")) {
            continue;
        }

        std::string file_name = get_android_dt_dir() + dp->d_name;

        android::base::ReadFileToString(file_name, &dt_file);
        std::replace(dt_file.begin(), dt_file.end(), ',', '.');

        InitPropertySet("ro.boot."s + dp->d_name, dt_file);
    }
}

static void ProcessKernelCmdline() {
    bool for_emulator = false;
    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
        if (key == "qemu") {
            for_emulator = true;
        } else if (StartsWith(key, "androidboot.")) {
            InitPropertySet("ro.boot." + key.substr(12), value);
        }
    });

    if (for_emulator) {
        ImportKernelCmdline([&](const std::string& key, const std::string& value) {
            // In the emulator, export any kernel option with the "ro.kernel." prefix.
            InitPropertySet("ro.kernel." + key, value);
        });
    }
}

void PropertyInit() {
    selinux_callback cb;
    cb.func_audit = PropertyAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
        LOG(FATAL) << "Failed to load serialized property info file";
    }

    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    ProcessKernelDt();
    ProcessKernelCmdline();

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    ExportKernelBootProps();

    PropertyLoadBootDefaults();
}

static void HandleInitSocket() {
    auto message = ReadMessage(init_socket);
    if (!message) {
@@ -1075,7 +1147,7 @@ static void PropertyServiceThread() {
}

void StartPropertyService(int* epoll_socket) {
    property_set("ro.property_service.version", "2");
    InitPropertySet("ro.property_service.version", "2");

    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
@@ -1095,11 +1167,6 @@ void StartPropertyService(int* epoll_socket) {
    listen(property_set_fd, 8);

    std::thread{PropertyServiceThread}.detach();

    property_set = [](const std::string& key, const std::string& value) -> uint32_t {
        android::base::SetProperty(key, value);
        return 0;
    };
}

}  // namespace init
Loading