Android4.4 LightsService使用笔记

框架图

image.png

一、先看framework层提供的API

LightsService中的一些注释

路径frameworks/base/services/java/com/android/server/LightsService.java

//灯的定义
public static final int LIGHT_ID_BACKLIGHT = 0;//LCD背光灯
public static final int LIGHT_ID_KEYBOARD = 1;//键盘灯
public static final int LIGHT_ID_BUTTONS = 2;//按键灯
public static final int LIGHT_ID_BATTERY = 3;//电池灯
public static final int LIGHT_ID_NOTIFICATIONS = 4;//消息通知等灯
public static final int LIGHT_ID_ATTENTION = 5;//警示灯
public static final int LIGHT_ID_BLUETOOTH = 6;//蓝牙灯
public static final int LIGHT_ID_WIFI = 7;//Wifi灯

//闪烁方式
public static final int LIGHT_FLASH_NONE = 0;//不闪烁
public static final int LIGHT_FLASH_TIMED = 1;//根据设定的时间闪烁
public static final int LIGHT_FLASH_HARDWARE = 2;//根据硬件控制的闪烁

 /**
  * Light brightness is managed by a user setting.
  * 背光亮度由用户设置
  */
 public static final int BRIGHTNESS_MODE_USER = 0;

 /**
  * Light brightness is managed by a light sensor.
  *背光亮度由光线传感器自动设置
  */
 public static final int BRIGHTNESS_MODE_SENSOR = 1;

//存放Light对象的数组
private final Light mLights[] = new Light[LIGHT_ID_COUNT];

//构造方法
LightsService(Context context) {
    mNativePointer = init_native();
    mContext = context;
    ServiceManager.addService("hardware",mLegacyFlashlightHack);
    for (int i = 0; i < LIGHT_ID_COUNT; i++) {
        mLights[i] = new Light(i);
    }
}

/*
*根据id获取对应的Light对象
*/
public Light getLight(int id) {
    return mLights[id];
}

//本地方法
private static native int init_native();  //初始化
private static native void finalize_native(int ptr); //释放资源 

//设置灯参数
private static native void setLight_native(int ptr, int light, int color, int mode,
        int onMS, int offMS, int brightnessMode);

LightsService中的内部类Light的一些注释

Light的方法定义有如下几个:

public void setBrightness(int brightness);
public void setBrightness(int brightness, int brightnessMode);
public void setColor(int color);
public void setFlashing(int color, int mode, int onMS, int offMS);
public void pulse();
public void pulse(int color, int onMS);
public void turnOff();

Light类的方法详解

setBrightness

/*
*这个方法是设置灯的亮度值,它最终会将这个亮度brightness(取值范围0~255)转换成色彩值
*color(ARGB)。我们知道颜色都是三基色RGB组成的,我们要控制的灯基本上都是单色的,
*要么是红色,要么是绿色。所以在转换过程中,是将亮度值对应的数值分别设置到RGB三色
*中,也就是每一个单色上都设置为跟亮度相同的数值,这样无论控制的是什么颜色的灯,都会将
*亮度值设置为brightness。其中0xff000000表示的是ARGB模式,A表示的是透明度。在实际操作过程中,只要亮度值大于0,设置任意数值都会起到相同的作用,都能够达到点亮灯的效果。
*/
public void setBrightness(int brightness, int brightnessMode) {
    synchronized (this) {
        int color = brightness & 0x000000ff;
        color = 0xff000000 | (color << 16) | (color << 8) | color;
        setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
    }
}

setLightLocked

这个方法名中之所以要加上Locked,是因为在调用这个方法的时候都要放在关键字synchronized的作用域中

 private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
     if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {
         if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
                 + Integer.toHexString(color));
         mColor = color;
         mMode = mode;
         mOnMS = onMS;
         mOffMS = offMS;
         setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
     }
 }

例如其它方法都同步调用setLightLocked方法:

public void turnOff() {
    synchronized (this) {
        setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, 0);
    }
}

private void stopFlashing() {
    synchronized (this) {
        setLightLocked(mColor, LIGHT_FLASH_NONE, 0, 0, BRIGHTNESS_MODE_USER);
    }
}

二、JNI层com_android_server_LightsService.cpp

路径:frameworks/base/services/jni/com_android_server_LightsService.cpp
LightsService调用了LightsService.cpp的本地方法,下面看一下该类。

com_android_server_LightsService.cpp的一些注释

//关键引入hardware模块和lights模块接口
#include <hardware/hardware.h>
#include <hardware/lights.h>

//入口函数,注册方法
int register_android_server_LightsService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LightsService",
            method_table, NELEM(method_table));
}

//注册方法表
static JNINativeMethod method_table[] = {
    { "init_native", "()I", (void*)init_native },
    { "finalize_native", "(I)V", (void*)finalize_native },
    { "setLight_native", "(IIIIIII)V", (void*)setLight_native },
};

//LIGHT_ID定义,与LightsService.java中定义的必须一致
// These values must correspond with the LIGHT_ID constants in LightsService.java
enum {
    LIGHT_INDEX_BACKLIGHT = 0,
    LIGHT_INDEX_KEYBOARD = 1,
    LIGHT_INDEX_BUTTONS = 2,
    LIGHT_INDEX_BATTERY = 3,
    LIGHT_INDEX_NOTIFICATIONS = 4,
    LIGHT_INDEX_ATTENTION = 5,
    LIGHT_INDEX_BLUETOOTH = 6,
    LIGHT_INDEX_WIFI = 7,
    LIGHT_COUNT
};

//定义结构体
struct Devices {
    light_device_t* lights[LIGHT_COUNT];
};

//初始化方法
static jint init_native(JNIEnv *env, jobject clazz)
{
    int err;
    hw_module_t* module;
    Devices* devices;
    //为Light设备分配内存
    devices = (Devices*)malloc(sizeof(Devices));

    //根据模块ID获取Lights模块,LIGHTS_HARDWARE_MODULE_ID定义在lights.h中
    //#define LIGHTS_HARDWARE_MODULE_ID "lights"
    err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
    if (err == 0) {  //err=0表示获取Lights模块成功
        //Light设备内存的初始化,根据不同的LIGHT_ID获取对应的设备,LIGHT_ID_XX 定义在lights.h中
        devices->lights[LIGHT_INDEX_BACKLIGHT]
                = get_device(module, LIGHT_ID_BACKLIGHT);
        devices->lights[LIGHT_INDEX_KEYBOARD]
                = get_device(module, LIGHT_ID_KEYBOARD);
        devices->lights[LIGHT_INDEX_BUTTONS]
                = get_device(module, LIGHT_ID_BUTTONS);
        devices->lights[LIGHT_INDEX_BATTERY]
                = get_device(module, LIGHT_ID_BATTERY);
        devices->lights[LIGHT_INDEX_NOTIFICATIONS]
                = get_device(module, LIGHT_ID_NOTIFICATIONS);
        devices->lights[LIGHT_INDEX_ATTENTION]
                = get_device(module, LIGHT_ID_ATTENTION);
        devices->lights[LIGHT_INDEX_BLUETOOTH]
                = get_device(module, LIGHT_ID_BLUETOOTH);
        devices->lights[LIGHT_INDEX_WIFI]
                = get_device(module, LIGHT_ID_WIFI);
    } else {
        memset(devices, 0, sizeof(Devices));
      }
     return (jint)devices;
 }
//根据ID获取设备
static light_device_t* get_device(hw_module_t* module, char const* name)
{
    int err;
    hw_device_t* device;
    //打开设备,err=0表示打开成功
    err = module->methods->open(module, name, &device);
    if (err == 0) {
        return (light_device_t*)device;
    } else {
        return NULL;
    }
}
   
/*设置Light设备的参数
* env: jni环境变量
* clazz:调用该本地方法的java对象
*  ptr: light设备的地址指针
*  light: light的id值
* colorARGB:颜色值
* flashMode:闪烁模式(不闪烁/根据设置时间闪烁/硬件控制闪烁)
* onMS: 闪烁时light打开时长
* offMS: 闪烁时light关闭时长
* brightnessMode:背光亮度模式(传感器/用户设置)
*/
static void setLight_native(JNIEnv *env, jobject clazz, int ptr,
        int light, int colorARGB, int flashMode, int onMS, int offMS, int brightnessMode)
{
    Devices* devices = (Devices*)ptr;  //根据指针获取到实例对象
    light_state_t state;

    //判断light id值是否合法必须>=0并且小于LIGHT_COUNT,设备的light对象不能为空
    if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) {
        return ;
    }
    
    //生成参数写入到state对象
    memset(&state, 0, sizeof(light_state_t));
    state.color = colorARGB;
    state.flashMode = flashMode;
    state.flashOnMS = onMS;
    state.flashOffMS = offMS;
    state.brightnessMode = brightnessMode;

    {
        ALOGD_IF_SLOW(50, "Excessive delay setting light");
        //将state对象设置到light对象中
        devices->lights[light]->set_light(devices->lights[light], &state);
    }
}

//销毁LightService
static void finalize_native(JNIEnv *env, jobject clazz, int ptr)
{   
    Devices* devices = (Devices*)ptr;
    if (devices == NULL) {  //对象指向空地址
        return;
    }
    free(devices);//释放内存空间
}                                                                               

三、Lights Hardware Interfaces

由com_android_server_LightsService.cpp源码中可知,它引入了hardware中的lights.h
路径hardware/libhardware/include/hardware/lights.h

lights.h的一些注释

//关键定义,暂时不知道是做什么用的
#ifndef ANDROID_LIGHTS_INTERFACE_H
#define ANDROID_LIGHTS_INTERFACE_H
/**
 * The id of this module  定义模块ID
 */
#define LIGHTS_HARDWARE_MODULE_ID "lights"

/*
 * These light IDs correspond to logical lights, not physical.
 * So for example, if your INDICATOR light is in line with your
 * BUTTONS, it might make sense to also light the INDICATOR
 * light to a reasonable color when the BUTTONS are lit.
  * 这些LIGHT ID对应着逻辑上的Light,非物理上的。例如,如果您的指示灯与按钮一致,那么当按钮被点亮时,将指示灯点亮到一个合理的颜色也是有意义的
 */
#define LIGHT_ID_BACKLIGHT          "backlight"
#define LIGHT_ID_KEYBOARD           "keyboard"
#define LIGHT_ID_BUTTONS            "buttons"
#define LIGHT_ID_BATTERY            "battery"
#define LIGHT_ID_NOTIFICATIONS      "notifications"
#define LIGHT_ID_ATTENTION          "attention"

/*
 * These lights aren't currently supported by the higher
 * layers, but could be someday, so we have the constants
 * here now.
 * 这几个类在将来可以能会被使用,先定义在这里
 */
#define LIGHT_ID_BLUETOOTH          "bluetooth"
#define LIGHT_ID_WIFI               "wifi"

//关键引入 
#include <hardware/hardware.h>

//模式定义
 /* 
  * Flash modes for the flashMode field of light_state_t. 
  * 不闪烁
  */
 
 #define LIGHT_FLASH_NONE            0
 
 /**
  * To flash the light at a given rate, set flashMode to LIGHT_FLASH_TIMED,
  * and then flashOnMS should be set to the number of milliseconds to turn
  * the light on, followed by the number of milliseconds to turn the light
  * off.
  * 根据设定的时间闪烁
  */
 #define LIGHT_FLASH_TIMED           1
 
 /**
  * To flash the light using hardware assist, set flashMode to
  * the hardware mode.
  * 由硬件控制进行闪烁
  */
 #define LIGHT_FLASH_HARDWARE        2
 
 /**
  * Light brightness is managed by a user setting. 由用户设定背光亮度
  */
 #define BRIGHTNESS_MODE_USER        0
 
 /**
  * Light brightness is managed by a light sensor. 由传感器自动设置背光亮度
  */
 #define BRIGHTNESS_MODE_SENSOR      1

//定义light_state_t 结构体   
/**
 * The parameters that can be set for a given light.
 *  定义了Light对象所有可以设置的参数
 * Not all lights must support all parameters.  If you
 * can do something backward-compatible, you should.
 */
struct light_state_t {
    /**
     * The color of the LED in ARGB.
     * LED灯ARGS模式的颜色值
     * Do your best here.
     *   - If your light can only do red or green, if they ask for blue,
     *     you should do green.
     *   如果你的灯只能做红色或绿色,如果他们要求蓝色,你应该设置为绿色
     *   - If you can only do a brightness ramp, then use this formula:
     * 如果你只做亮度渐变,那么使用下面这个公式:
     *      unsigned char brightness = ((77*((color>>16)&0x00ff))
     *              + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
     *   - If you can only do on or off, 0 is off, anything else is on.
     *  如果你只是开关LED,0为关闭,其它值 为打开
     * The high byte should be ignored.  Callers will set it to 0xff (which
     * would correspond to 255 alpha).
     */
    unsigned int color;

    /**
     * See the LIGHT_FLASH_* constants
     */
    int flashMode;  //闪烁模式
    int flashOnMS;  //点亮时间
    int flashOffMS;  //熄灭时间

    /**
     * Policy used by the framework to manage the light's brightness.
     * 框架使用的策略来管理灯光的亮度
     * Currently the values are BRIGHTNESS_MODE_USER and BRIGHTNESS_MODE_SENSOR.
     */
    int brightnessMode;
};

//定义light_device_t 结构体
struct light_device_t {
    struct hw_device_t common;
    /**
     * Set the provided lights to the provided values.
     *
     * Returns: 0 on succes, error code on failure. 设置light参数
     */
    int (*set_light)(struct light_device_t* dev, struct light_state_t const* state);
};      
/*函数名称前面加指针符号“*”,代表它是函数指针。
*函数指针是一个指向函数的指针,函数指针表示一个函数的入口地址。使用函数指针的好处就是在处理“在运行时根据数据的具体状态来选择相应的处理方式”这种需求时更加灵活。    
*/                                                                  

hardware.h

light.h中引用了hardware.h
路径hardware/libhardware/include/hardware/hardware.h

hardwared.h的一些注释

只看一些重要的方法

/**
 * Get the module info associated with a module by id.
 * 根据module id获取对应的module,例如获取Lights module的ID 就是LIGHTS_HARDWARE_MODULE_ID
 * @return: 0 == success, <0 == error and *module == NULL
 */
int hw_get_module(const char *id, const struct hw_module_t **module);

//根据class id获取module,这里暂时不讨论
int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module);

//重要结构体有三个
struct hw_module_t;  //数据结构的字段必须以hw_module_t开头
struct hw_module_methods_t;  //然后公共方法
struct hw_device_t;  //最后是模块特定信息

//第一个重要结构体
/**
 * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
 * and the fields of this data structure must begin with hw_module_t
 * followed by module specific information.
 *  每个硬件模块都必须有一个名为HAL_MODULE_INFO_SYM的数据结构,该数据结构的字段必须以
 * hw_module_t开头,然后是模块特定的信息。
 */
typedef struct hw_module_t {
    /** tag must be initialized to HARDWARE_MODULE_TAG */
      uint32_t tag;  //TAG信息
     uint16_t module_api_version;  //module 的API版本信息
     #define version_major module_api_version
    uint16_t hal_api_version;  //hal层的API版本信息
    #define version_minor hal_api_version

    /** Identifier of module */
    const char *id;  //module id

    /** Name of this module */
    const char *name;  //module名称

    /** Author/owner/implementor of the module */
    const char *author;  //作者

    /** Modules methods */
    struct hw_module_methods_t* methods;  //定义的方法

    /** module's dso */
    void* dso;

    /** padding to 128 bytes, reserved for future use */
    uint32_t reserved[32-7];

} hw_module_t;
//可以看到,hw_module_t定义了一些版本,作者和定义的方法等相关信息

//第二个结构体
typedef struct hw_module_methods_t {
    /** Open a specific device */
    //函数指针
    int (*open)(const struct hw_module_t* module, const char* id, struct hw_device_t** device);
} hw_module_methods_t;

//第三个结构体
/**
 * Every device data structure must begin with hw_device_t
 * followed by module specific public methods and attributes.
 * 每个设备数据结构都必须以hw_device_t开头,然后是模块特定的公共方法和属性。
 */
typedef struct hw_device_t {
    /** tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t tag;
    uint32_t version;

    /** reference to the module this device belongs to */
    struct hw_module_t* module;

    /** padding reserved for future use */
    uint32_t reserved[12];

    /** Close this device */ 
    int (*close)(struct hw_device_t* device);//函数指针

} hw_device_t;

抽象接口实现 lights.c

路径vendor/xxx/open-source/libs/liblights/lights.c
注意:linux中的所有操作都是通过文件的形式,所以LED的最终操作也是通过读写节点文件来实现的
该类中提供了操作LED灯节点文件的方法,通过向节点文件写入数据,位于kernel下面的LED灯的驱动程序会检测到文件的改动,进而对LED的状态根据写入的值进行重新设定。

lights.c的一些注释

//定义所有的LED灯类型
//all lights
enum {
    RED_LED,  //红灯
    GREEN_LED,  //绿灯
    BLUE_LED,  //蓝灯
    LCD_BACKLIGHT,  //LCD背光灯
    BUTTONS_LED,  //按键背光灯
    NUM_LEDS,  //LED灯个数 
};

//定义LED灯可操作的类型,实际对应了多个节点文件
struct led {
    struct led_prop brightness;  //设置亮度
    struct led_prop high_time;  
    struct led_prop low_time;
    struct led_prop rising_time;
    struct led_prop falling_time;
    struct led_prop on_off;  //开关
};

//定义生成的节点路径
//light nodes
struct led leds[NUM_LEDS] = {
    [RED_LED] = {
        .brightness = { "/sys/class/leds/red/brightness", -1},
        .high_time = { "/sys/class/leds/red_bl/high_time", -1},
        .low_time = { "/sys/class/leds/red_bl/low_time", -1},
        .rising_time = { "/sys/class/leds/red_bl/rising_time", -1},
        .falling_time = { "/sys/class/leds/red_bl/falling_time", -1},
        .on_off = { "/sys/class/leds/red_bl/on_off", -1},
    },
    [GREEN_LED] = {
        .brightness = { "/sys/class/leds/green/brightness", -1},
        .high_time = { "/sys/class/leds/green_bl/high_time", -1},
        .low_time = { "/sys/class/leds/green_bl/low_time", -1},
        .rising_time = { "/sys/class/leds/green_bl/rising_time", -1},
        .falling_time = { "/sys/class/leds/green_bl/falling_time", -1},
        .on_off = { "/sys/class/leds/green_bl/on_off", -1},
    },
    [BLUE_LED] = {
        .brightness = { "/sys/class/leds/blue/brightness", -1},
        .high_time = { "/sys/class/leds/blue_bl/high_time", -1},
        .low_time = { "/sys/class/leds/blue_bl/low_time", -1},
        .rising_time = { "/sys/class/leds/blue_bl/rising_time", -1},
        .falling_time = { "/sys/class/leds/blue_bl/falling_time", -1},
        .on_off = { "/sys/class/leds/blue_bl/on_off", -1},
    },
    [LCD_BACKLIGHT] = {
        .brightness = { "/sys/class/backlight/sprd_backlight/brightness", -1},
    },
    [BUTTONS_LED] = {
        .brightness = {"/sys/class/leds/keyboard-backlight/brightness", -1},
    },
};
                                                                              
//初始化所有节点
void init_globals(void)
{
    int i;

    for (i = 0; i < NUM_LEDS; ++i) {
        init_prop(&leds[i].brightness);
        init_prop(&leds[i].high_time);
        init_prop(&leds[i].low_time);
        init_prop(&leds[i].rising_time);
        init_prop(&leds[i].falling_time)
        init_prop(&leds[i].on_off);
    }
}

//看一下与hardwared.h对应的结构体
/*第一个结构体
 * 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,
};

//第二个结构体,通过open_lights获取所有公用函数
static struct hw_module_methods_t lights_module_methods = {
    //open是一个函数指针,这里赋值为open_lights,open_lights函数下面有介绍
        .open =  open_lights,


//打开LED灯函数
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);

        ALOGV("file:%s, func:%s name=%s\n", __FILE__, __func__, name);

//从前面分析lights.h知道,set_light是一个函数指针,其作用就是这里根据不同的LED灯类型,设置不同的处理函数
        if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))
                set_light = set_light_backlight;
        else if (0 == strcmp(LIGHT_ID_KEYBOARD, name))
                set_light = set_light_keyboard;
        else if (0 == strcmp(LIGHT_ID_BUTTONS, name))
                set_light = set_light_buttons;
        else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
                set_light = set_light_leds_notifications;
        else if (0 == strcmp(LIGHT_ID_ATTENTION, name))
                set_light = set_light_leds_attention;
        else if (0 == strcmp(LIGHT_ID_BATTERY,name))
                set_light = set_light_leds_notifications;
        else
                return -EINVAL;

        pthread_once(&g_init, init_g_lock);
//      pthread_once(&g_init, init_globals);

        struct light_device_t *dev = malloc(sizeof(struct light_device_t));
        memset(dev, 0, sizeof(*dev));

        dev->common.tag = HARDWARE_DEVICE_TAG;  //设置TAG信息
        dev->common.version = 0;  //设置版本信息
        dev->common.module = (struct hw_module_t *)module;  //设置module
        dev->common.close = (int (*)(struct hw_device_t *))close_lights;  //设置关闭方法
        dev->set_light = set_light;  //设置设置方法

        *device = (struct hw_device_t *)dev;

        return 0;
}

//写节点的函数,通过该方法可以向节点写入数据 
static int write_int(struct led_prop *prop, int value)
{       
        int fd;
        static int already_warned;
        
        already_warned = 0;
        
        ALOGE("file:%s, func:%s, path=%s, value=%d\n", __FILE__, __func__, prop->filename, value);
     //   fd = open(path, O_RDWR);
          fd = open(prop->filename, O_RDWR);
        
        if (fd >= 0) {
                char buffer[20];
                int bytes = sprintf(buffer, "%d\n", value);
                int amt = write(fd, buffer, bytes);
                close(fd); 
                return amt == -1 ? -errno : 0;
        } else {
                if (already_warned == 0) {                        ALOGE("file:%s, func:%s, failed to open %s,fd = %d\n", __FILE__, __func__, prop->filename,fd);                     
                        already_warned = 1;
                }
                return -errno;
        }
}
//其它的就是一些设置方法了,简述一下
static int set_light_backlight(struct light_device_t *dev, struct light_state_t const *state)
static int set_light_keyboard(struct light_device_t* dev, struct light_state_t const* state)
static int is_lit(struct light_state_t const* state)
static int set_light_buttons(struct light_device_t* dev, struct light_state_t const* state)
static int set_breath_light(struct light_device_t* dev, struct light_state_t const* state)
....                                                                       

该文件可能需要根据具体使用的LED灯设备节点进行修改,可参考我另一篇文章Android4.4移植-LED灯驱动适配

四、驱动层

驱动文件一般位于kernel下面,根据不同的硬件厂商而有所不同。
驱动程序会监测上面提到的所有节点文件是否被写入,如果节点文件有写入不同的值,那么驱动程序会读取该值 ,并依据具体情况设定不同的LED灯状态。驱动文件详情不再详述。

如有问题,欢迎交流!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容