[Android P][init]on property字段注意事项

最近在Androd P平台上开发一个功能,有一段逻辑需要通过system_server进程对proc/vm/sys/下的某个节点进行写值操作。考虑到时间有限,目前最有效的方法就是借助init.rc中的on property触发机制来完成。

大致逻辑如下:

system_server:在必要处调用SystemProperties.set("sys.sysctl.xxxx","1"); //此处参考ProcessList.java中对与sys.sysctl.extra_free_kbytes的处理方式;

配置相关SELinux权限;

在init.target.rc/init.project.rc等类似的rc文件中添加:

on property sys.sysctl.xxxx=1

    write /proc/sys/vm/xxx 1

一切都看上去那么美好,而且与sys.sysctl.extra_free_kbytes的处理方式几乎一模一样。

然而运行后才发现,这个触发器并没有任何效果!

初步推断有以下几种可能:

1、SELinux配置不正确/SystemProperties.set没有生效或者报错;

2、on property触发器没有接收到/没有处理此属性;

3、write节点被权限拒绝;

通过adb root/setenforce 0以后验证,属性是可以写成功的,因此排除1的可能;

通过adb root/setenforce 0以后直接echo节点,也是可以生效的,因此排除3的可能;

那么剩下的,就是2了:

查看dmesg发现开机解析rc文件时,有如下输出:

[    4.165212] .(3)[1:init]init: /vendor/etc/init/hw/init.project.rc: 116: ParseTriggers() failed: unexported property tigger found: sys.sysctl.xxx

倒跟代码发现判断逻辑是这样的:

system/core/init/action_parser.cpp:

Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
                                     std::map<std::string, std::string>* property_triggers) {
    const static std::string prop_str("property:");
    std::string prop_name(trigger.substr(prop_str.length()));
    size_t equal_pos = prop_name.find('=');
    if (equal_pos == std::string::npos) {
        return Error() << "property trigger found without matching '='";
    }

    std::string prop_value(prop_name.substr(equal_pos + 1));
    prop_name.erase(equal_pos);

    if (!IsActionableProperty(subcontext, prop_name)) {
        //此处报错
        return Error() << "unexported property tigger found: " << prop_name;
    }

    if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) {
        return Error() << "multiple property triggers found for same property";
    }
    return Success();
}

而 函数IsActionableProperty就在同一文件下:

bool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) {
    static bool enabled = GetBoolProperty("ro.actionable_compatible_property.enabled", false);

    if (subcontext == nullptr || !enabled) {
        return true;
    }

    if (kExportedActionableProperties.count(prop_name) == 1) {
        return true;
    }
    for (const auto& prefix : kPartnerPrefixes) {
        if (android::base::StartsWith(prop_name, prefix)) {
            return true;
        }
    }
    return false;
}

kExportedActionableProperties与kPartnerPrefixes是定义在同目录下stable_properties.h中:

static constexpr const char* kPartnerPrefixes[] = {
    "init.svc.vendor.", "ro.vendor.", "persist.vendor.", "vendor.", "init.svc.odm.", "ro.odm.",
    "persist.odm.",     "odm.",       "ro.boot.",
};

static const std::set<std::string> kExportedActionableProperties = {
    "dev.bootcomplete",
    "init.svc.console",
    "init.svc.mediadrm",
    "init.svc.surfaceflinger",
    "init.svc.zygote",
    "persist.bluetooth.btsnoopenable",
    "persist.sys.crash_rcu",
    "persist.sys.usb.usbradio.config",
    "persist.sys.zram_enabled",
    "ro.board.platform",
    "ro.bootmode",
    "ro.build.type",
    "ro.crypto.state",
    "ro.crypto.type",
    "ro.debuggable",
    "sys.boot_completed",
    "sys.boot_from_charger_mode",
    "sys.retaildemo.enabled",
    "sys.shutdown.requested",
    "sys.usb.config",
    "sys.usb.configfs",
    "sys.usb.ffs.mtp.ready",
    "sys.usb.ffs.ready",
    "sys.user.0.ce_available",
    "sys.vdso",
    "vold.decrypt",
    "vold.post_fs_data_done",
    "vts.native_server.on",
    "wlan.driver.status",
};

可以看到,我们的属性sys.sysctl.xxx既不在kExportedActionableProperties中,也不是kPartnerPrefixes所列的前缀开头,因此不能被识别为可以用来进行触发操作的有效的属性;

到这里,似乎都说通了,解决方法就是添加白名单,或者修改属性的前缀。

但是!有个问题,为什么sys.sysctl.extra_free_kbytes就可以呢?同样不在kExportedActionableProperties中,也不是kPartnerPrefixes所列的前缀开头。为什么就可以呢?

根据打log分析,发现该属性在此处便返回了:

    if (subcontext == nullptr || !enabled) {
        return true;
    }

后者默认为true,应该是Google VTS要求项,因此不会变,也不能变;

那么就是前者为nullptr的原因咯。

逆向跟踪,看看subcontext在什么情况下为nullptr;

同样还在action_parser.cpp中:

Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
                                           const std::string& filename, int line) {
    std::vector<std::string> triggers(args.begin() + 1, args.end());
    if (triggers.size() < 1) {
        return Error() << "Actions must have a trigger";
    }

    Subcontext* action_subcontext = nullptr;
    if (subcontexts_) {
        for (auto& subcontext : *subcontexts_) {
            //此处判断这个rc文件(带完整路径)的前缀
            if (StartsWith(filename, subcontext.path_prefix())) {
                action_subcontext = &subcontext;
                break;
            }
        }
    }

    std::string event_trigger;
    std::map<std::string, std::string> property_triggers;

    if (auto result = ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);
        !result) {
        return Error() << "ParseTriggers() failed: " << result.error();
    }

    auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,
                                           property_triggers);

    action_ = std::move(action);
    return Success();
}

可以看到这里有做判断,只有当filename符合path_prefix()的要求时,action_subcontext才会被赋值,而action_subcontext就是后续传到ParseTrigger中的subcontext;

而这个前缀定义是在同目录下的subcontext.cpp中:

const std::string kInitContext = "u:r:init:s0";
const std::string kVendorContext = "u:r:vendor_init:s0";

const char* const paths_and_secontexts[2][2] = {
    {"/vendor", kVendorContext.c_str()},
    {"/odm", kVendorContext.c_str()},
};

至此,真相大白,我修改的init.project.rc存放在vendor下,因此会被上述的kExportedActionableProperties与kPartnerPrefixes所限制,而sys.sysctl.extra_free_kbytes是写在/init.rc,属于/根挂载点下的文件,不会被过滤;

鉴于Google对于VTS的要求愈发严格,因此不建议将自己新增的属性添加到/根挂载点的init.rc或/system下的init.xxx.rc中;

而是遵循kPartnerPrefixes的要求,使用符合规范的前缀的属性,老老实实添加在vendor下的各种rc文件中吧;

以上,踩坑经历讲述完毕。

发布了15 篇原创文章 · 获赞 14 · 访问量 9442

猜你喜欢

转载自blog.csdn.net/u014175785/article/details/92992931