Android系统10 RK3399 init进程启动(三十五) 属性文件介绍和生成过程

 配套系列教学视频链接:

      安卓系列教程之ROM系统开发-百问100ask

说明

系统:Android10.0

设备: FireFly RK3399 (ROC-RK3399-PC-PLUS)

前言

init进程在启动会去加载后缀为.prop的属性问文件, 将属性文件中的属性加载到共享内存中, 这样系统就有了默认的一些属性, 本章节介绍常见属性文件,以及这些文件是如何生成的。


一,属性文件介绍

init进程启动代码system/core/init/property_service.cpp,会调用如下函数来加载属性:

void property_load_boot_defaults(bool load_debug_prop) {
    // 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
    // property files, regardless of if they are "ro." properties or not.
    std::map<std::string, std::string> properties;
    if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
        // Try recovery path
        if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
            // Try legacy path
            load_properties_from_file("/default.prop", nullptr, &properties);
        }
    }
    load_properties_from_file("/system/build.prop", nullptr, &properties);
    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
        load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
    } else {
        load_properties_from_file("/odm/default.prop", nullptr, &properties);
        load_properties_from_file("/odm/build.prop", nullptr, &properties);
    }
    load_properties_from_file("/product/build.prop", nullptr, &properties);
    load_properties_from_file("/product_services/build.prop", nullptr, &properties);
    load_properties_from_file("/factory/factory.prop", "ro.*", &properties);

    if (load_debug_prop) {
        LOG(INFO) << "Loading " << kDebugRamdiskProp;
        load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
    }

    for (const auto& [name, value] : properties) {
        std::string error;
        if (PropertySet(name, value, &error) != PROP_SUCCESS) {
            LOG(ERROR) << "Could not set '" << name << "' to '" << value
                       << "' while loading .prop files" << error;
        }
    }

    property_initialize_ro_product_props();
    property_derive_build_fingerprint();

    update_sys_usb_config();
}

以上会显示加载属性文件的先后顺序, 文件里面的内容大概如下格式:

ro.actionable_compatible_property.enabled=true

ro.postinstall.fstab.prefix=/system

ro.secure=1

security.perf_harden=1

常见的属性文件介绍如下:

/system/etc/prop.default

设置ro.debuggable, vm,dex优化相关属性,如果没有该文件,就尝试加载/prop.default,再没有就加载/default.prop

/system/build.prop

系统编译属性,如ro.system.build.date系统编译时间,版本等,

/vendor/default.prop

厂商默认属性, 如:

ro.bionic.arch=arm64

ro.bionic.cpu_variant=cortex-a53

ro.bionic.2nd_arch=arm

ro.bionic.2nd_cpu_variant=cortex-a15

/vendor/build.prop

厂商编译属性, 如厂商型号,cpu abi列表:

ro.vendor.product.cpu.abilist32=armeabi-v7a,armeabi

ro.vendor.product.cpu.abilist64=arm64-v8a

ro.product.board=rk30sdk

ro.board.platform=rk3399

/odm/etc/build.prop

odm编译属性,记录编译时间, 产品信息, 支持的cpu结构:

ro.odm.product.cpu.abilist=arm64-v8a,armeabi-v7a,armeabi

ro.odm.product.cpu.abilist32=armeabi-v7a,armeabi

ro.odm.product.cpu.abilist64=arm64-v8a

ro.odm.build.id=QD4A.200805.003

ro.odm.build.tags=release-keys

ro.odm.build.type=userdebug

ro.product.odm.brand=rockchip

ro.product.odm.device=rk3399_roc_pc_plus

ro.product.odm.manufacturer=rockchip

ro.product.odm.model=ROC-RK3399-PC-PLUS

ro.product.odm.name=rk3399_roc_pc_plus

Android Q以下版本是找/odm/default.prop和/odm/build.prop

/product/build.prop

产品编译属性:也是记录编译时间, 产品信息:

ro.product.product.brand=rockchip

ro.product.product.device=rk3399_roc_pc_plus

ro.product.product.manufacturer=rockchip

ro.product.product.model=ROC-RK3399-PC-PLUS

ro.product.product.name=rk3399_roc_pc_plus

 二, 属性文件内容的组成

属性文件中的属性是来自哪里的呢?比如/system/build.prop,其实可以直接查看里面的内容,是由以下信息组成的:

build/make/tools/buildinfo_common.sh

build/make/tools/buildinfo.sh

两个脚本会根据build/core/version_defaults.mk里获取各种变量值来赋值给不同属性

device/rockchip/rk3399/rk3399_roc_pc_plus/system.prop

实际是一个变量system_prop_file包含的值

ADDITIONAL_BUILD_PROPERTIES

主要是PRODUCT_PROPERTY_OVERRIDES这个变量赋值, 而这个变量就是我们开发时经常用于自定义属性的, 一般在产品配置目录下定义, 另外就是main.mk中默认增加的属性

 

 在./build/make/core/main.mk中可以清晰的看到ADDITIONAL_BUILD_PROPERTIES里面包含了什么:

179 ifneq ($(BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED), true)

 180   ADDITIONAL_BUILD_PROPERTIES += $(PRODUCT_PROPERTY_OVERRIDES)

 181 else

 182   ifndef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE

 183     ADDITIONAL_BUILD_PROPERTIES += $(PRODUCT_PROPERTY_OVERRIDES)

 184   endif

 185 endif

231 ifneq ($(TARGET_BUILD_VARIANT),user)

 232   ADDITIONAL_BUILD_PROPERTIES += ro.bionic.ld.warning=1

 233 else

 234 # Enable it for user builds as long as they are not final.

 235 ifneq ($(PLATFORM_VERSION_CODENAME),REL)

 236   ADDITIONAL_BUILD_PROPERTIES += ro.bionic.ld.warning=1

 237 endif

 238 endif

 239

240 ADDITIONAL_BUILD_PROPERTIES += ro.treble.enabled=${PRODUCT_FULL_TREBLE}

 其实PRODUCT_PROPERTY_OVERRIDES就比较重要, 我们定制新的默认属性,经常在mk文件中给这个变量赋值。

 /vendor/build.prop内容是由以下信息组成的:

特定的几个产品和厂商相关属性

build/make/tools/buildinfo_common.sh

BOOTIMAGE_BUILD_PROPERTIES

ADDITIONAL VENDOR BUILD PROPERTIES

三, 属性文件的生成过程

至于这些文件具体是如何生成的,可以直接在build/make/core/Makefile文件中搜索:

build.prop, vendor build.prop, odm build.prop, product build.prop, vendor default.prop等关键词,就可以知道如下:


195 # -----------------------------------------------------------------

196 # prop.default

197

198 BUILDINFO_SH := build/make/tools/buildinfo.sh

199 BUILDINFO_COMMON_SH := build/make/tools/buildinfo_common.sh

build/make/tools/buildinfo.sh脚本:

#!/bin/bash

echo "# begin build properties"
echo "# autogenerated by buildinfo.sh"

echo "ro.build.id=$BUILD_ID"
echo "ro.build.display.id=$BUILD_DISPLAY_ID"
echo "ro.build.version.incremental=$BUILD_NUMBER"
echo "ro.build.version.sdk=$PLATFORM_SDK_VERSION"
echo "ro.build.version.preview_sdk=$PLATFORM_PREVIEW_SDK_VERSION"
echo "ro.build.version.preview_sdk_fingerprint=$PLATFORM_PREVIEW_SDK_FINGERPRINT"
echo "ro.build.version.codename=$PLATFORM_VERSION_CODENAME"
echo "ro.build.version.all_codenames=$PLATFORM_VERSION_ALL_CODENAMES"
echo "ro.build.version.release=$PLATFORM_VERSION"
echo "ro.build.version.security_patch=$PLATFORM_SECURITY_PATCH"
echo "ro.build.version.base_os=$PLATFORM_BASE_OS"
echo "ro.build.version.min_supported_target_sdk=$PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION"
echo "ro.build.date=`$DATE`"
echo "ro.build.date.utc=`$DATE +%s`"
echo "ro.build.type=$TARGET_BUILD_TYPE"
echo "ro.build.user=$BUILD_USERNAME"
echo "ro.build.host=$BUILD_HOSTNAME"
echo "ro.build.tags=$BUILD_VERSION_TAGS"
echo "ro.build.flavor=$TARGET_BUILD_FLAVOR"
if [ -n "$BOARD_BUILD_SYSTEM_ROOT_IMAGE" ] ; then
  echo "ro.build.system_root_image=$BOARD_BUILD_SYSTEM_ROOT_IMAGE"
fi
if [ -n "$AB_OTA_UPDATER" ] ; then
  echo "ro.build.ab_update=$AB_OTA_UPDATER"
fi

# These values are deprecated, use "ro.product.cpu.abilist"
# instead (see below).
echo "# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete,"
echo "# use ro.product.cpu.abilist instead."
echo "ro.product.cpu.abi=$TARGET_CPU_ABI"
if [ -n "$TARGET_CPU_ABI2" ] ; then
  echo "ro.product.cpu.abi2=$TARGET_CPU_ABI2"
fi
echo "ro.product.cpu.abilist=$TARGET_CPU_ABI_LIST"
echo "ro.product.cpu.abilist32=$TARGET_CPU_ABI_LIST_32_BIT"
echo "ro.product.cpu.abilist64=$TARGET_CPU_ABI_LIST_64_BIT"

if [ -n "$PRODUCT_DEFAULT_LOCALE" ] ; then
  echo "ro.product.locale=$PRODUCT_DEFAULT_LOCALE"
fi
echo "ro.wifi.channels=$PRODUCT_DEFAULT_WIFI_CHANNELS"

echo "# ro.build.product is obsolete; use ro.product.device"
echo "ro.build.product=$TARGET_DEVICE"

echo "# Do not try to parse description or thumbprint"
echo "ro.build.description=$PRIVATE_BUILD_DESC"
if [ -n "$BUILD_THUMBPRINT" ] ; then
  echo "ro.build.thumbprint=$BUILD_THUMBPRINT"
fi

echo "# end build properties"

 build/make/tools/buildinfo_common.sh脚本

#!/bin/bash

partition="$1"

if [ "$#" -ne 1 ]; then
  echo "Usage: $0 <partition>" 1>&2
  exit 1
fi

echo "# begin common build properties"
echo "# autogenerated by $0"

echo "ro.${partition}.build.date=`$DATE`"
echo "ro.${partition}.build.date.utc=`$DATE +%s`"
echo "ro.${partition}.build.fingerprint=$BUILD_FINGERPRINT"
echo "ro.${partition}.build.id=$BUILD_ID"
echo "ro.${partition}.build.tags=$BUILD_VERSION_TAGS"
echo "ro.${partition}.build.type=$TARGET_BUILD_TYPE"
echo "ro.${partition}.build.version.incremental=$BUILD_NUMBER"
echo "ro.${partition}.build.version.release=$PLATFORM_VERSION"
echo "ro.${partition}.build.version.sdk=$PLATFORM_SDK_VERSION"

echo "ro.product.${partition}.brand=$PRODUCT_BRAND"
echo "ro.product.${partition}.device=$PRODUCT_DEVICE"
echo "ro.product.${partition}.manufacturer=$PRODUCT_MANUFACTURER"
echo "ro.product.${partition}.model=$PRODUCT_MODEL"
echo "ro.product.${partition}.name=$PRODUCT_NAME"

echo "# end common build properties"

system/build.prop文件的生成过程, 在build/make/core/Makefile中有如下描述


391 ifdef TARGET_SYSTEM_PROP
392 system_prop_file := $(TARGET_SYSTEM_PROP)
393 else
394 system_prop_file := $(wildcard $(TARGET_DEVICE_DIR)/system.prop)
395 endif
396 $(intermediate_system_build_prop): $(BUILDINFO_SH) $(BUILDINFO_COMMON_SH) $(INTERNAL_BUILD_ID_MAKEFILE) $(BUILD_SYSTEM)/version_defaults.mk $(system_prop_file) $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(API_FINGERPRINT)
397 	@echo Target buildinfo: $@
398 	@mkdir -p $(dir $@)
399 	$(hide) echo > $@
400 ifneq ($(PRODUCT_OEM_PROPERTIES),)
401 	$(hide) echo "#" >> $@; \
402 	        echo "# PRODUCT_OEM_PROPERTIES" >> $@; \
403 	        echo "#" >> $@;
404 	$(hide) $(foreach prop,$(PRODUCT_OEM_PROPERTIES), \
405 	    echo "import /oem/oem.prop $(prop)" >> $@;)
406 endif
407 	$(hide) PRODUCT_BRAND="$(PRODUCT_SYSTEM_BRAND)" \
408 	        PRODUCT_MANUFACTURER="$(PRODUCT_SYSTEM_MANUFACTURER)" \
409 	        PRODUCT_MODEL="$(PRODUCT_SYSTEM_MODEL)" \
410 	        PRODUCT_NAME="$(PRODUCT_SYSTEM_NAME)" \
411 	        PRODUCT_DEVICE="$(PRODUCT_SYSTEM_DEVICE)" \
412 	        $(call generate-common-build-props-with-product-vars-set,system,$@)
413 	$(hide) TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)" \
414 	        TARGET_BUILD_FLAVOR="$(TARGET_BUILD_FLAVOR)" \
415 	        TARGET_DEVICE="$(TARGET_DEVICE)" \
416 	        PRODUCT_DEFAULT_LOCALE="$(call get-default-product-locale,$(PRODUCT_LOCALES))" \
417 	        PRODUCT_DEFAULT_WIFI_CHANNELS="$(PRODUCT_DEFAULT_WIFI_CHANNELS)" \
418 	        PRIVATE_BUILD_DESC="$(PRIVATE_BUILD_DESC)" \
419 	        BUILD_ID="$(BUILD_ID)" \
420 	        BUILD_DISPLAY_ID="$(BUILD_DISPLAY_ID)" \
421 	        DATE="$(DATE_FROM_FILE)" \
422 	        BUILD_USERNAME="$(BUILD_USERNAME)" \
423 	        BUILD_HOSTNAME="$(BUILD_HOSTNAME)" \
424 	        BUILD_NUMBER="$(BUILD_NUMBER_FROM_FILE)" \
425 	        BOARD_BUILD_SYSTEM_ROOT_IMAGE="$(BOARD_BUILD_SYSTEM_ROOT_IMAGE)" \
426 	        AB_OTA_UPDATER="$(AB_OTA_UPDATER)" \
427 	        PLATFORM_VERSION="$(PLATFORM_VERSION)" \
428 	        PLATFORM_SECURITY_PATCH="$(PLATFORM_SECURITY_PATCH)" \
429 	        PLATFORM_BASE_OS="$(PLATFORM_BASE_OS)" \
430 	        PLATFORM_SDK_VERSION="$(PLATFORM_SDK_VERSION)" \
431 	        PLATFORM_PREVIEW_SDK_VERSION="$(PLATFORM_PREVIEW_SDK_VERSION)" \
432 	        PLATFORM_PREVIEW_SDK_FINGERPRINT="$$(cat $(API_FINGERPRINT))" \
433 	        PLATFORM_VERSION_CODENAME="$(PLATFORM_VERSION_CODENAME)" \
434 	        PLATFORM_VERSION_ALL_CODENAMES="$(PLATFORM_VERSION_ALL_CODENAMES)" \
435 	      PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION="$(PLATFORM_MIN_SUPPORTED_TARGE_SDK_VERSION)" \
436 	        BUILD_VERSION_TAGS="$(BUILD_VERSION_TAGS)" \
437 	        $(if $(OEM_THUMBPRINT_PROPERTIES),BUILD_THUMBPRINT="$(BUILD_THUMBPRINT_FROM_FILE)") \
438 	        TARGET_CPU_ABI_LIST="$(TARGET_CPU_ABI_LIST)" \
439 	        TARGET_CPU_ABI_LIST_32_BIT="$(TARGET_CPU_ABI_LIST_32_BIT)" \
440 	        TARGET_CPU_ABI_LIST_64_BIT="$(TARGET_CPU_ABI_LIST_64_BIT)" \
441 	        TARGET_CPU_ABI="$(TARGET_CPU_ABI)" \
442 	        TARGET_CPU_ABI2="$(TARGET_CPU_ABI2)" \
443 	        bash $(BUILDINFO_SH) >> $@
444 	$(hide) $(foreach file,$(system_prop_file), \
445 	    if [ -f "$(file)" ]; then \
446 	        echo Target buildinfo from: "$(file)"; \
447 	        echo "" >> $@; \
448 	        echo "#" >> $@; \
449 	        echo "# from $(file)" >> $@; \
450 	        echo "#" >> $@; \
451 	        cat $(file) >> $@; \
452 	        echo "# end of $(file)" >> $@; \
453 	    fi;)
454 	$(if $(FINAL_BUILD_PROPERTIES), \
455 	    $(hide) echo >> $@; \
456 	            echo "#" >> $@; \
457 	            echo "# ADDITIONAL_BUILD_PROPERTIES" >> $@; \
458 	            echo "#" >> $@; )
459 	$(hide) $(foreach line,$(FINAL_BUILD_PROPERTIES), \
460 	    echo "$(line)" >> $@;)
461 	$(hide) build/make/tools/post_process_props.py $@ $(PRODUCT_SYSTEM_PROPERTY_BLACKLIST)
462 
463 build_desc :=
464 
465 ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))
466 INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
467 else
468 INSTALLED_RECOVERYIMAGE_TARGET :=
469 endif
470 
471 $(INSTALLED_BUILD_PROP_TARGET): $(intermediate_system_build_prop) $(INSTALLED_RECOVERYIMAGE_TARGET)
472 	@echo "Target build info: $@"
473 	$(hide) grep -v 'ro.product.first_api_level' $(intermediate_system_build_prop) > $@
474 

 上面大体是如下过程:

1,buildinfo.sh和buildinfo_common.sh脚本会输出很多property值给build.prop
2,system_prop_file变量代表的文件的内容会被到build.prop文件里去,system_prop_file代表的文件可以由TARGET_SYSTEM_PROP变量定义,也可以是device目录下的system.prop文件, 在build目录搜索结果如下:
./build/make/core/Makefile:404:system_prop_file := $(TARGET_SYSTEM_PROP)
./build/make/core/Makefile:406:system_prop_file := $(wildcard $(TARGET_DEVICE_DIR)/system.prop)
3,ADDITIONAL_BUILD_PROPERTIES主要是PRODUCT_PROPERTY_OVERRIDES这个变量赋值, 而这个变量就是我们开发时经常用于自定义属性的, 一般在产品配置目录下定义, 另外就是main.mk中默认增加的属性
4,将out/target/product/${target_device}/obj/ETC/system_build_prop_intermediates里的build.prop拷贝到out/target/product/${target_device}/system/目录下

 大家在看build/make/core/Makefile时可能涉及到一下函数, 很多函数都是在build/core/definitions.mk中定义:

collapse-pairs

作用是将ADDITIONAL_DEFAULT_PROPERTIES和PRODUCT_DEFAULT_PROPERTY_OVERRIDES两个变量里的所有赋值语句’=’两端的空格去掉

uniq-pairs-by-first-component

作用就是对ADDITIONAL_DEFAULT_PROPERTIES里的赋值语句去重,如果有发现对同一个property_name赋值多次,则只保留第一个值。

post_process_props.py

用法:

post_process_props.py $@ (PRODUCT_SYSTEM_PROPERTY_BLACKLIST)

作用: 

删除default.prop文件中指定的property(在函数的第二个参数中指定,没有则表明不指定)并且检查每个property名字和值的长度是否超过最大值,在这个python脚本中还可以根据需求再修改一次property值作为预置。

 /vendor/build.prop文件生成的过程:

​

482 ifdef property_overrides_split_enabled

483 FINAL_VENDOR_BUILD_PROPERTIES += \

484     $(call collapse-pairs, $(PRODUCT_PROPERTY_OVERRIDES))

485 FINAL_VENDOR_BUILD_PROPERTIES := $(call uniq-pairs-by-first-component, \

486     $(FINAL_VENDOR_BUILD_PROPERTIES),=)

487 endif  # property_overrides_split_enabled

488

489 $(INSTALLED_VENDOR_BUILD_PROP_TARGET): $(BUILDINFO_COMMON_SH) $(intermediate_system_build_prop)

490 @echo Target vendor buildinfo: $@

491 @mkdir -p $(dir $@)

492 $(hide) echo > $@

493 ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)

494 $(hide) echo ro.boot.dynamic_partitions=true >> $@

495 endif

496 ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true)

497 $(hide) echo ro.boot.dynamic_partitions_retrofit=true >> $@

498 endif

499 $(hide) grep 'ro.product.first_api_level' $(intermediate_system_build_prop) >> $@ || true

500 $(hide) echo ro.vendor.build.security_patch="$(VENDOR_SECURITY_PATCH)">>$@

501 $(hide) echo ro.vendor.product.cpu.abilist="$(TARGET_CPU_ABI_LIST)">>$@

502 $(hide) echo ro.vendor.product.cpu.abilist32="$(TARGET_CPU_ABI_LIST_32_BIT)">>$@

503 $(hide) echo ro.vendor.product.cpu.abilist64="$(TARGET_CPU_ABI_LIST_64_BIT)">>$@

504 $(hide) echo ro.product.board="$(TARGET_BOOTLOADER_BOARD_NAME)">>$@

505 $(hide) echo ro.board.platform="$(TARGET_BOARD_PLATFORM)">>$@

506 $(hide) echo ro.hwui.use_vulkan="$(TARGET_USES_VULKAN)">>$@

507 ifdef TARGET_SCREEN_DENSITY

508 $(hide) echo ro.sf.lcd_density="$(TARGET_SCREEN_DENSITY)">>$@

509 endif

510 $(hide) $(call generate-common-build-props,vendor,$@)

511 $(hide) echo "#" >> $@; \

512         echo "# BOOTIMAGE_BUILD_PROPERTIES" >> $@; \

513         echo "#" >> $@;

514 $(hide) echo ro.bootimage.build.date=`$(DATE_FROM_FILE)`>>$@

515 $(hide) echo ro.bootimage.build.date.utc=`$(DATE_FROM_FILE) +%s`>>$@

516 $(hide) echo ro.bootimage.build.fingerprint="$(BUILD_FINGERPRINT_FROM_FILE)">>$@

517 $(hide) echo "#" >> $@; \

518         echo "# ADDITIONAL VENDOR BUILD PROPERTIES" >> $@; \

519         echo "#" >> $@;

520 $(hide) cat $(INSTALLED_ANDROID_INFO_TXT_TARGET) | grep 'require version-' | sed -e 's/require version-/ro.build.expect./g' >> $@

521 ifdef property_overrides_split_enabled

522 $(hide) $(foreach line,$(FINAL_VENDOR_BUILD_PROPERTIES), \

523     echo "$(line)" >> $@;)

524 endif  # property_overrides_split_enabled

525 $(hide) build/make/tools/post_process_props.py $@ $(PRODUCT_VENDOR_PROPERTY_BLACKLIST)

​

 四, 总结

了解属性文件的生成过程, 就可以很方便的去定制系统默认的属性, 比如修改system.prop文件, 新增变量PRODUCT_PROPERTY_OVERRIDES。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旗浩QH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值