基于 Light 介绍安卓 8.0 HAL 变化

前言

学习笔记,简单介绍了 light 在 Android 8.0 上的整个调用流程
更新上添加下调用流程线,将各个文件串起来

HIDL 简介

一张图开场
在这里插入图片描述
在这里插入图片描述

	其实主要是就是将以前那种 Java-> Jni -> Hal -> Kernel 的结构变成了
	
	Java -> Jni -> Binder 客户端 ====== Binder 通信 ======> Binder 服务端 -> Hal -> Kernel 

 	将 framework 与 Hal 之间的交互变成了 CS 结构了。

插播下 Java Binder 服务编写框图:
在这里插入图片描述

C++ Binder 服务编写框图:
在这里插入图片描述

启动流程

 // SystemServer.java (frameworks\base\services\java\com\android\server)
 import com.android.server.lights.LightsService;
 
 startBootstrapServices()  
    # 此函数可参考 BatteryService 流程分析
    mSystemServiceManager.startService(LightsService.class);
            // SystemServiceManager.java (frameworks\base\services\core\java\com\android\server)
            startService(Class<T> serviceClass)
                # 获取类名
                final String name = serviceClass.getName();
                # 获得构造函数: public LightsService(Context context)
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                # 调用构造函数,创建类对象
                service = constructor.newInstance(mContext);
                        // LightsService.java (frameworks\base\services\core\java\com\android\server\lights)
                        // 构造函数:
                        LightsService(Context context)
                            super(context);

                            for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) {
                                mLights[i] = new LightImpl(i);
                            }
                
                
                # 添加到 SystemService 链表中管理,并启动服务
                startService(service);
                    mServices.add(service);
                    service.onStart();
                            // LightsService.java (frameworks\base\services\core\java\com\android\server\lights)
                            onStart()
                                // 这是向 systemservice.java 注册了一些回调,只让其调用使用?
                                // Publish the service so it is only accessible to the system process
                                // 让其只能在 system 进程中访问?
                                //      ./base/services/core/java/com/android/server/power/PowerManagerService.java:736:            
                                //          mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
                                publishLocalService(LightsManager.class, mService);

HIDL 使用

// 接口文件:
//  Z:\work\A306_eng\src\hardware\interfaces\light\2.0\ILight.hal
    package android.hardware.light@2.0;

    interface ILight {

        /**
         * Set the provided lights to the provided values.
         *
         * @param type logical light to set
         * @param state describes what the light should look like.
         * @return status result of applying state transformation.
         */
        setLight(Type type, LightState state) generates (Status status);

        /**
         * Discover what indicator lights are available.
         *
         * @return types list of available lights
         */
        getSupportedTypes() generates (vec<Type> types);

    };

// 编译文件:
// Z:\work\A306_eng\src\hardware\interfaces\light\2.0\Android.bp
    filegroup {
        name: "android.hardware.light@2.0_hal",
        srcs: [
            "types.hal",
            "ILight.hal",
        ],
    }

    genrule {
        name: "android.hardware.light@2.0_genc++",
        tools: ["hidl-gen"],
        cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.light@2.0",
        srcs: [
            ":android.hardware.light@2.0_hal",
        ],
        out: [
            "android/hardware/light/2.0/types.cpp",
            "android/hardware/light/2.0/LightAll.cpp",
        ],
    }
    
    // 编译工具: out/host/linux-x86/bin/hidl-gen 
    // 生成文件:
    //      Z:\work\A306_eng\src\out\soong\.intermediates\hardware\interfaces\light\2.0\android.hardware.light@2.0_genc++\gen\android\hardware\light\2.0\LightAll.cpp
            getService(): 获取 servicemanager 中的对应 binder 服务 
            BnHwLight::onTransact()# 服务器端,调用具体子类提供服务操作硬件
                _hidl_mImpl->setLight(type, *state);
                
            BpHwLight::setLight(Type type, const LightState& state): # 客户端,提供远程调用服务端接口
                    1. 填充 Parcel 数据
                    2. 调用 remote() 通过 binder 调用到服务器端执行
                    _hidl_err = remote()->transact(1 /* setLight */, _hidl_data, &_hidl_reply);

Jni 流程: HIDL 客户端使用

// com_android_server_lights_LightsService.cpp (frameworks\base\services\core\jni)
static void setLight_native( JNIEnv* /* env */, jobject /* clazz */, jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS,jint brightnessMode) 
    
    # HAL 服务获取: HIDL 接口,从 servicemanager 中获取 Light 服务
    sp<ILight> hal = LightHal::associate();
            // will return the hal if it exists the first time.
            sLight = ILight::getService();
                // Z:\work\A306_eng\src\out\soong\.intermediates\hardware\interfaces\light\2.0\android.hardware.light@2.0_genc++\gen\android\hardware\light\2.0\LightAll.cpp
                ::android::sp<ILight> ILight::getService(const std::string &serviceName, const bool getStub) 
                        const sp<::android::hidl::manager::V1_0::IServiceManager> sm = defaultServiceManager();
                        Return<Transport> transportRet = sm->getTransport(ILight::descriptor, serviceName);
                        
                       
                        if (getStub || vintfPassthru || vintfLegacy) {
                            
                            # 获取 PassthroughServiceManager 服务 
                            const sp<::android::hidl::manager::V1_0::IServiceManager> pm = getPassthroughServiceManager();
              
                            Return<sp<::android::hidl::base::V1_0::IBase>> ret = pm->get(ILight::descriptor, serviceName);
                                    // ServiceManagement.cpp (system\libhidl\transport)
                                    struct PassthroughServiceManager : IServiceManager {
                                                        Return<sp<IBase>> get(const hidl_string& fqName,
                                                                              const hidl_string& name)
                                    {
                                        # PassthroughServiceManager 的 get(const hidl_string& fqName, const hidl_string& name)
                                        # 函数根据传入的 fqName=(android.hardware.cameraProvider@2.4::ICameraProvider"),
                                        
                                        # 获取当前的接口名ICameraProvider,
                                        #   拼接出后面需要载入的函数名:HIDL_FETCH_ICameraProvider 和库名字:
                                        #           android.hardware.camera.provider@2.4-impl.so
                                        
                                        std::string stdFqName(fqName.c_str());
                                        //fqName looks like android.hardware.foo@1.0::IFoo
                                        size_t idx = stdFqName.find("::");

                                        if (idx == std::string::npos ||
                                                idx + strlen("::") + 1 >= stdFqName.size()) {
                                            LOG(ERROR) << "Invalid interface name passthrough lookup: " << fqName;
                                            return nullptr;
                                        }

                                        std::string packageAndVersion = stdFqName.substr(0, idx);
                                        std::string ifaceName = stdFqName.substr(idx + strlen("::"));

                                        const std::string prefix = packageAndVersion + "-impl";
                                        const std::string sym = "HIDL_FETCH_" + ifaceName;
                                        
                                        

                                        # 接着通过 dlopen 载入 /vendor/lib/hw/android.hardware.light@2.0-impl.so;
                                        #   通过 dlsym 载入 HIDL_FETCH_ICameraProvider 函数
                                        # 至此正式进入cameraprovider的implement

                                        
                                        std::vector<std::string> libs = search(path, prefix, ".so");
                                        handle = dlopen(fullPath.c_str(), dlMode);
                                        IBase* (*generator)(const char* name);
                                        *(void **)(&generator) = dlsym(handle, sym.c_str());
                                        IBase *interface = (*generator)(name.c_str());
                                                #################################################################
                                                # 调用 HIDL 服务入口函数
                                                #################################################################
                                                // Z:\work\A306_eng\src\hardware\interfaces\light\2.0\default\Light.cpp
                                                ILight* HIDL_FETCH_ILight(const char* /* name */) {
                                                    std::map<Type, light_device_t*> lights;

                                                    for(auto const &pair : kLogicalLights) {
                                                        Type type = pair.first;
                                                        const char* name = pair.second;

                                                        light_device_t* light = getLightDevice(name);
                                                                    #############################################
                                                                    # 老 HAL 库加载
                                                                    #############################################
                                                                    light_device_t* getLightDevice(const char* name)
                                                                        int ret = hw_get_module (LIGHTS_HARDWARE_MODULE_ID, &hwModule);
                                                                                    // Lights.c (hardware\qcom\display\liblight)
                                                                                        struct hw_module_t HAL_MODULE_INFO_SYM = {
                                                                                            .tag = HARDWARE_MODULE_TAG,
                                                                                            .version_major = 1,
                                                                                            .version_minor = 0,
                                                                                            .id = LIGHTS_HARDWARE_MODULE_ID,
                                                                                            .name = "lights Module",
                                                                                            .author = "Google, Inc.",
                                                                                            .methods = &lights_module_methods,
                                                                                        };

                                                                                        static struct hw_module_methods_t lights_module_methods = {
                                                                                            .open =  open_lights,
                                                                                        };

                                                                        
                                                                        ret = hwModule->methods->open(hwModule, name,reinterpret_cast<hw_device_t**>(&lightDevice));
                                                                                // Light.cpp (vendor\qcom\proprietary\fastmmi\module\light)
                                                                                open_lights(const struct hw_module_t* module, char const* name,struct hw_device_t** device)
                                                                                    set_light = set_light_notifications;
                                                                                            handle_speaker_battery_locked(dev);
                                                                                                set_speaker_light_locked(dev, &g_notification);
                                                                                                    // wangjun@wind-mobi.com 20180111 begein
                                                                                                    // 闪烁之前清灯状态
                                                                                                    write_int(RED_LED_FILE, 0);
                                                                                                    write_int(GREEN_LED_FILE, 0);
                                                                                                    write_int(BLUE_LED_FILE, 0);
                                                                                                            # 往 /sys/class/leds/green/brightness 这类的结点写亮度值
                                                                                                            fd = open(path, O_RDWR);
                                                                                                            int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
                                                                                                            ssize_t amt = write(fd, buffer, (size_t)bytes);
                                                                                                            close(fd);
                                                                                                    // wangjun@wind-mobi.com 20180111 end
                                                                                                
                                                                                                
                                                        if (light != nullptr) {
                                                            lights[type] = light;
                                                        }
                                                    }

                                                    if (lights.size() == 0) {
                                                        // Log information, but still return new Light.
                                                        // Some devices may not have any lights.
                                                        ALOGI("Could not open any lights.");
                                                    }

                                                    return new Light(std::move(lights));
                                                }
                        
                        
    Type type = static_cast<Type>(light);
    // 构造 LightState 状态类 
    LightState state = constructState( colorARGB, flashMode, onMS, offMS, brightnessMode);
    ALOGD_IF_SLOW(50, "Excessive delay setting light");
    
    # HIDL 接口向 HAL 下发灯状态给他处理
    Return<Status> ret = hal->setLight(type, state);
    
    # 处理通信返回值
    processReturn(ret, type, state);

HIDL 服务端

// Z:\work\A306_eng\src\hardware\interfaces\light\2.0\default\Android.mk
    include $(CLEAR_VARS)
    LOCAL_MODULE_RELATIVE_PATH := hw
    LOCAL_PROPRIETARY_MODULE := true
    LOCAL_MODULE := android.hardware.light@2.0-service
    LOCAL_INIT_RC := android.hardware.light@2.0-service.rc
    LOCAL_SRC_FILES := \
        service.cpp \

    LOCAL_SHARED_LIBRARIES := \
        liblog \
        libcutils \
        libdl \
        libbase \
        libutils \
        libhardware \

    LOCAL_SHARED_LIBRARIES += \
        libhidlbase \
        libhidltransport \
        android.hardware.light@2.0 \

    include $(BUILD_EXECUTABLE)

    // 服务启动:
    // Z:\work\A306_eng\src\hardware\interfaces\light\2.0\default\android.hardware.light@2.0-service.rc
    service light-hal-2-0 /vendor/bin/hw/android.hardware.light@2.0-service
        class hal
        user system
        group system    
    
        // Z:\work\A306_eng\src\hardware\interfaces\light\2.0\default\service.cpp
        int main() {
            return defaultPassthroughServiceImplementation<ILight>();
                        /
                        // LegacySupport.h (system\libhidl\transport\include\hidl)
                        defaultPassthroughServiceImplementation(std::string name, size_t maxThreads = 1)
                            configureRpcThreadpool(maxThreads, true);
                                configureBinderRpcThreadpool(maxThreads, callerWillJoin);
                                    ProcessState::self()->setThreadPoolConfiguration(maxThreads, callerWillJoin /*callerJoinsPool*/);
                                
                                
                            status_t result = registerPassthroughServiceImplementation<Interface>(name);                                    
                                                registerPassthroughServiceImplementation( std::string name = "default")
                                                        sp<Interface> service = Interface::getService(name, true /* getStub */);
                                                        status_t status = service->registerAsService(name);
                                                
                            joinRpcThreadpool();
                                joinBinderRpcThreadpool();
                                    IPCThreadState::self()->joinThreadPool();
            
            
        }

相关文件

下面将从上到下介绍:

0.【Java 安卓LED服务类】LightsService.java

public class LightsService extends SystemService {
	
	// 下一级 Jni 接口
	static native void setLight_native(int light, int color, int mode,
        int onMS, int offMS, int brightnessMode);
}

1.【JNI 客户端实现】com_android_server_lights_LightsService.cpp

	static const JNINativeMethod method_table[] = {
	    { "setLight_native", "(IIIIII)V", (void*)setLight_native },
	};

	static void setLight_native(
	        JNIEnv* /* env */,
	        jobject /* clazz */,
	        jint light,
	        jint colorARGB,
	        jint flashMode,
	        jint onMS,
	        jint offMS,
	        jint brightnessMode) {
	
	    if (!validate(light, flashMode, brightnessMode)) {
	        return;
	    }
	    sp<ILight> hal = LightHal::associate();
	
	    if (hal == nullptr) {
	        return;
	    }
	
	    Type type = static_cast<Type>(light);
	    LightState state = constructState(
	        colorARGB, flashMode, onMS, offMS, brightnessMode);
	
	    {
	        ALOGD_IF_SLOW(50, "Excessive delay setting light");
			
			// 下一级入口 
	        Return<Status> ret = hal->setLight(type, state);
	        processReturn(ret, type, state);
	    }
	}

2.【HIDL 客户】ILight.与 HIDL 服务库通信.hal

	定义了 HIDL 接口 
	package android.hardware.light@2.0;

	interface ILight {
	
	    /**
	     * Set the provided lights to the provided values.
	     *
	     * @param type logical light to set
	     * @param state describes what the light should look like.
	     * @return status result of applying state transformation.
	     */
	    setLight(Type type, LightState state) generates (Status status);
	
	    /**
	     * Discover what indicator lights are available.
	     *
	     * @return types list of available lights
	     */
	    getSupportedTypes() generates (vec<Type> types);
	
	};

3.【HIDL 客户】LightAll.cpp.调用 HIDL 服务库

::android::status_t BnHwLight::onTransact(
    uint32_t _hidl_code,
    const ::android::hardware::Parcel &_hidl_data,
    ::android::hardware::Parcel *_hidl_reply,
    uint32_t _hidl_flags,
    TransactCallback _hidl_cb)
			// 下一级接口
			Status _hidl_out_status = _hidl_mImpl->setLight(type, *state);

4.【HIDL 服务】android.hardware.light@2.0-service.启动注册 HIDL 服务库

	service light-hal-2-0 /vendor/bin/hw/android.hardware.light@2.0-service
	    class hal
	    user system
	    group system

4.【HIDL 服务】service.cpp.向上注册 HIDL 服务库

	int main() {
		    return defaultPassthroughServiceImplementation<ILight>();    
		            /
		            // LegacySupport.h (system\libhidl\transport\include\hidl)
		            defaultPassthroughServiceImplementation(std::string name, size_t maxThreads = 1)
		                configureRpcThreadpool(maxThreads, true);
		                    configureBinderRpcThreadpool(maxThreads, callerWillJoin);
		                        ProcessState::self()->setThreadPoolConfiguration(maxThreads, callerWillJoin /*callerJoinsPool*/);
		                    
		                    
		                status_t result = registerPassthroughServiceImplementation<Interface>(name);                                    
		                                    registerPassthroughServiceImplementation( std::string name = "default")
		                                            sp<Interface> service = Interface::getService(name, true /* getStub */);
		                                            status_t status = service->registerAsService(name);
		                                    
		                joinRpcThreadpool();
		                    joinBinderRpcThreadpool();
		                        IPCThreadState::self()->joinThreadPool();

5.【HIDL 服务】Light.h

	namespace android {
	namespace hardware {
	namespace light {
	namespace V2_0 {
	namespace implementation {
	
	using ::android::hardware::light::V2_0::ILight;
	using ::android::hardware::light::V2_0::LightState;
	using ::android::hardware::light::V2_0::Status;
	using ::android::hardware::light::V2_0::Type;
	using ::android::hardware::Return;
	using ::android::hardware::Void;
	using ::android::hardware::hidl_vec;
	using ::android::hardware::hidl_string;
	using ::android::sp;
	
	struct Light : public ILight {
	    Light(std::map<Type, light_device_t*> &&lights);
	
	    // Methods from ::android::hardware::light::V2_0::ILight follow.
	    Return<Status> setLight(Type type, const LightState& state)  override;
	    Return<void> getSupportedTypes(getSupportedTypes_cb _hidl_cb)  override;
	
	private:
	    std::map<Type, light_device_t*> mLights;
	};
	
	extern "C" ILight* HIDL_FETCH_ILight(const char* name);
	
	}  // namespace implementation
	}  // namespace V2_0
	}  // namespace light
	}  // namespace hardware
	}  // namespace android

6.【HIDL 服务】Light.cpp.加载调用传统库

	ILight* HIDL_FETCH_ILight(const char* /* name */) {
	   std::map<Type, light_device_t*> lights;
	
	    for(auto const &pair : kLogicalLights) {
	        Type type = pair.first;
	        const char* name = pair.second;
	
			// 加载传统 HAL 库 
	        light_device_t* light = getLightDevice(name);
	
	        if (light != nullptr) {
	            lights[type] = light;
	        }
	    }
	
	    if (lights.size() == 0) {
	        // Log information, but still return new Light.
	        // Some devices may not have any lights.
	        ALOGI("Could not open any lights.");
	    }
	
	    return new Light(std::move(lights));
	}


	Light::Light(std::map<Type, light_device_t*> &&lights)
	  : mLights(std::move(lights)) {}
	
	// Methods from ::android::hardware::light::V2_0::ILight follow.
	Return<Status> Light::setLight(Type type, const LightState& state)  {
	    auto it = mLights.find(type);
	
	    if (it == mLights.end()) {
	        return Status::LIGHT_NOT_SUPPORTED;
	    }
	
	    light_device_t* hwLight = it->second;
	
	    light_state_t legacyState {
	        .color = state.color,
	        .flashMode = static_cast<int>(state.flashMode),
	        .flashOnMS = state.flashOnMs,
	        .flashOffMS = state.flashOffMs,
	        .brightnessMode = static_cast<int>(state.brightnessMode),
	    };
	
	    int ret = hwLight->set_light(hwLight, &legacyState);
	
	    switch (ret) {
	        case -ENOSYS:
	            return Status::BRIGHTNESS_NOT_SUPPORTED;
	        case 0:
	            return Status::SUCCESS;
	        default:
	            return Status::UNKNOWN;
	    }
	}	

7.【HAL 实现】lights.c.传统 HAL 库

	/*
	 * The lights Module
	 */
	struct hw_module_t HAL_MODULE_INFO_SYM = {
	    .tag = HARDWARE_MODULE_TAG,
	    .version_major = 1,
	    .version_minor = 0,
	    .id = LIGHTS_HARDWARE_MODULE_ID,
	    .name = "lights Module",
	    .author = "Google, Inc.",
	    .methods = &lights_module_methods,
	};

/**
 * module methods
 */

/** Open a new instance of a lights device using name */
static int open_lights(const struct hw_module_t* module, char const* name,
        struct hw_device_t** device)
{
    int (*set_light)(struct light_device_t* dev,
            struct light_state_t const* state);

    if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
        char property[PROPERTY_VALUE_MAX];
        property_get("persist.extend.brightness", property, "0");

        if(!(strncmp(property, "1", PROPERTY_VALUE_MAX)) ||
           !(strncmp(property, "true", PROPERTY_VALUE_MAX))) {
            property_get("persist.display.max_brightness", property, "255");
            g_brightness_max = atoi(property);
            set_brightness_ext_init();
            set_light = set_light_backlight_ext;
        } else

			// 调用接口 set_light , 上层会调用到这里来
            set_light = set_light_backlight;
    } else if (0 == strcmp(LIGHT_ID_BATTERY, name))
        set_light = set_light_battery;
    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
        set_light = set_light_notifications;
    else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
        if (!access(BUTTON_FILE, F_OK)) {
          // enable light button when the file is present			  
          set_light = set_light_buttons;
        } else {
          return -EINVAL;
        }
    }
    else if (0 == strcmp(LIGHT_ID_ATTENTION, name))
        set_light = set_light_attention;
    else
        return -EINVAL;

    pthread_once(&g_init, init_globals);

    struct light_device_t *dev = malloc(sizeof(struct light_device_t));

    if(!dev)
        return -ENOMEM;

    memset(dev, 0, sizeof(*dev));

    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0;
    dev->common.module = (struct hw_module_t*)module;
    dev->common.close = (int (*)(struct hw_device_t*))close_lights;
    dev->set_light = set_light;

    *device = (struct hw_device_t*)dev;
    return 0;
}


# 更新灯则通过 sys/class/led/xxx/bright 来操作的
// char const*const RED_LED_FILE    = "/sys/class/leds/red/brightness";
set_speaker_light_locked(struct light_device_t* dev,
    struct light_state_t const* state)
		write_int(RED_LED_FILE, 0);
			int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
    		ssize_t amt = write(fd, buffer, (size_t)bytes);

8.【内核实现】leds-qpnp.c

【充电 CHG_LED 可用红灯配置流程】:
	// Msm8937-pmi8940-mtp.dtsi (kernel\msm-3.18\arch\arm64\boot\dts\qcom)
	    &pmi8940_charger {
	        qcom,battery-data = <&mtp_batterydata>;
	        qcom,chg-led-sw-controls;
	        qcom,chg-led-support;
	    };
	
	    解析使用位置:Qpnp-smbcharger.c (kernel\msm-3.18\drivers\power)
	        static int __init smbchg_init(void)
	            // static struct spmi_driver smbchg_driver = {
	            //     .driver		= {
	            //         .name		= "qpnp-smbcharger",
	            //         .owner		= THIS_MODULE,
	            //         .of_match_table	= smbchg_match_table,
	            //         .pm		= &smbchg_pm_ops,
	            //     },
	            //     .probe		= smbchg_probe,
	            //     .remove		= smbchg_remove,
	            //     .shutdown	= smbchg_shutdown,
	            // };
	            return spmi_driver_register(&smbchg_driver);
	
	
	        smbchg_probe(struct spmi_device *spmi)
	            smb_parse_dt(struct smbchg_chip *chip)
	                chip->cfg_chg_led_support =	of_property_read_bool(node, "qcom,chg-led-support");
	                
	            。。。    
	            if (chip->cfg_chg_led_support && chip->schg_version == QPNP_SCHG_LITE)    
	                rc = smbchg_register_chg_led(chip);
	                        chip->led_cdev.name = "red";
	                        chip->led_cdev.brightness_set = smbchg_chg_led_brightness_set;
	                        chip->led_cdev.brightness_get = smbchg_chg_led_brightness_get;
	                        rc = led_classdev_register(chip->dev, &chip->led_cdev);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值