电池数据相关结构体

 /system/core/healthd/include/healthd/healthd.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

struct healthd_config {
    int periodic_chores_interval_fast;
    int periodic_chores_interval_slow;

    android::String8 batteryStatusPath;
    android::String8 batteryHealthPath;
    android::String8 batteryPresentPath;
    android::String8 batteryCapacityPath;
    android::String8 batteryVoltagePath;
    android::String8 batteryTemperaturePath;
    android::String8 batteryTechnologyPath;
    android::String8 batteryCurrentNowPath;
    android::String8 batteryCurrentAvgPath;
    android::String8 batteryChargeCounterPath;
    android::String8 batteryFullChargePath;
    android::String8 batteryCycleCountPath;
    android::String8 batteryCapacityLevelPath;
    android::String8 batteryChargeTimeToFullNowPath;
    android::String8 batteryFullChargeDesignCapacityUahPath;

    int (*energyCounter)(int64_t *);
    int boot_min_cap;
    bool (*screen_on)(android::BatteryProperties *props);
    std::vector<android::String8> ignorePowerSupplyNames;
};

struct healthd_mode_ops {
    void (*init)(struct healthd_config *config);
    int (*preparetowait)(void);
    void (*heartbeat)(void);
    void (*battery_update)(struct android::BatteryProperties *props);
};

 /frameworks/native/services/batteryservice/include/batteryservice/BatteryService.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

namespace android {
struct BatteryProperties {
    bool chargerAcOnline;
    bool chargerUsbOnline;
    bool chargerWirelessOnline;
    int maxChargingCurrent;
    int maxChargingVoltage;
    int batteryStatus;
    int batteryHealth;
    bool batteryPresent;
    int batteryLevel;
    int batteryVoltage;
    int batteryTemperature;
    int batteryCurrent;
    int batteryCycleCount;
    int batteryFullCharge;
    int batteryChargeCounter;
    String8 batteryTechnology;
};
};

 /hardware/interfaces/health/1.0/default/convert.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

#include "include/hal_conversion.h"

namespace android {
namespace hardware {
namespace health {
namespace V1_0 {
namespace hal_conversion {
... ...
void convertToHealthInfo(const struct android::BatteryProperties *p,
                                 HealthInfo& info) {
    info.chargerAcOnline        = p->chargerAcOnline;
    info.chargerUsbOnline       = p->chargerUsbOnline;
    info.chargerWirelessOnline  = p->chargerWirelessOnline;
    info.maxChargingCurrent     = p->maxChargingCurrent;
    info.maxChargingVoltage     = p->maxChargingVoltage;
    info.batteryStatus          = static_cast<BatteryStatus>(p->batteryStatus);
    info.batteryHealth          = static_cast<BatteryHealth>(p->batteryHealth);
    info.batteryPresent         = p->batteryPresent;
    info.batteryLevel           = p->batteryLevel;
    info.batteryVoltage         = p->batteryVoltage;
    info.batteryTemperature     = p->batteryTemperature;
    info.batteryCurrent         = p->batteryCurrent;
    info.batteryCycleCount      = p->batteryCycleCount;
    info.batteryFullCharge      = p->batteryFullCharge;
    info.batteryChargeCounter   = p->batteryChargeCounter;
    info.batteryTechnology      = p->batteryTechnology;
}

void convertFromHealthInfo(const HealthInfo& info,
                                   struct android::BatteryProperties *p) {
    p->chargerAcOnline          = info.chargerAcOnline;
    p->chargerUsbOnline         = info.chargerUsbOnline;
    p->chargerWirelessOnline    = info.chargerWirelessOnline;
    p->maxChargingCurrent       = info.maxChargingCurrent;
    p->maxChargingVoltage       = info.maxChargingVoltage;
    p->batteryStatus            = static_cast<int>(info.batteryStatus);
    p->batteryHealth            = static_cast<int>(info.batteryHealth);
    p->batteryPresent           = info.batteryPresent;
    p->batteryLevel             = info.batteryLevel;
    p->batteryVoltage           = info.batteryVoltage;
    p->batteryTemperature       = info.batteryTemperature;
    p->batteryCurrent           = info.batteryCurrent;
    p->batteryCycleCount        = info.batteryCycleCount;
    p->batteryFullCharge        = info.batteryFullCharge;
    p->batteryChargeCounter     = info.batteryChargeCounter;
    p->batteryTechnology        = android::String8(info.batteryTechnology.c_str());
}

} // namespace hal_conversion
} // namespace V1_0
} // namespace health
} // namespace hardware
} // namespace android

 /system/core/healthd/BatteryMonitor.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

namespace android {

#define POWER_SUPPLY_SUBSYSTEM "power_supply"
#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
#define FAKE_BATTERY_CAPACITY 42
#define FAKE_BATTERY_TEMPERATURE 424

BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
    static SysfsStringEnumMap<int> supplyTypeMap[] = {
            {"Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},  //charger
            {"Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY},  //battery
            {"UPS", ANDROID_POWER_SUPPLY_TYPE_AC},
            {"Mains", ANDROID_POWER_SUPPLY_TYPE_AC},  //ac
            {"USB", ANDROID_POWER_SUPPLY_TYPE_USB},   //usb
            {"USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC},
            {"USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC},
            {"USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC},
            {"USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC},
            {"USB_C", ANDROID_POWER_SUPPLY_TYPE_AC},
            {"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC},
            {"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB},
            {"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
            {NULL, 0},
    };
    std::string buf;

    if (readFromFile(path, &buf) <= 0)
        return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;

    auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
    if (!ret) {
        KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
        *ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
    }

    return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
}

void BatteryMonitor::init(struct healthd_config *hc) {  //初始化每个property对应的节点路径
    String8 path;                                             //后续BatteryMonitor::getProperty去相应的节点读取数据
    char pval[PROPERTY_VALUE_MAX];

    mHealthdConfig = hc;
    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
    if (dir == NULL) {
        KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
    } else {
        struct dirent* entry;

        while ((entry = readdir(dir.get()))) {
            const char* name = entry->d_name;
            std::vector<String8>::iterator itIgnoreName;

            if (!strcmp(name, ".") || !strcmp(name, ".."))
                continue;

            itIgnoreName = find(hc->ignorePowerSupplyNames.begin(),
                                hc->ignorePowerSupplyNames.end(), String8(name));
            if (itIgnoreName != hc->ignorePowerSupplyNames.end())
                continue;

            // Look for "type" file in each subdirectory
            path.clear();
            path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);  //type/status/health等字符串需要和power_supply_attrs[]
            switch(readPowerSupplyType(path)) {
            case ANDROID_POWER_SUPPLY_TYPE_AC:
            case ANDROID_POWER_SUPPLY_TYPE_USB:
            case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
                path.clear();
                path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
                if (access(path.string(), R_OK) == 0)
                    mChargerNames.add(String8(name));
                break;

            case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
                // Some devices expose the battery status of sub-component like
                // stylus. Such a device-scoped battery info needs to be skipped
                // in BatteryMonitor, which is intended to report the status of
                // the battery supplying the power to the whole system.
                if (isScopedPowerSupply(name)) continue;
                mBatteryDevicePresent = true;

                if (mHealthdConfig->batteryStatusPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryStatusPath = path;
                }

                if (mHealthdConfig->batteryHealthPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryHealthPath = path;
                }

                if (mHealthdConfig->batteryPresentPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryPresentPath = path;
                }

                if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryCapacityPath = path;
                }

                if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/voltage_now",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0) {
                        mHealthdConfig->batteryVoltagePath = path;
                    }
                }

                if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/charge_full",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryFullChargePath = path;
                }

                if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/current_now",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryCurrentNowPath = path;
                }

                if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/cycle_count",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryCycleCountPath = path;
                }

                if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
                }

                if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryChargeTimeToFullNowPath = path;
                }

                if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
                }

                if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/current_avg",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryCurrentAvgPath = path;
                }

                if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/charge_counter",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryChargeCounterPath = path;
                }

                if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path, R_OK) == 0) {
                        mHealthdConfig->batteryTemperaturePath = path;
                    }
                }

                if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
                    path.clear();
                    path.appendFormat("%s/%s/technology",
                                      POWER_SUPPLY_SYSFS_PATH, name);
                    if (access(path, R_OK) == 0)
                        mHealthdConfig->batteryTechnologyPath = path;
                }

                break;

            case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
                break;
            }
        }
    }

    // Typically the case for devices which do not have a battery and
    // and are always plugged into AC mains.
    if (!mBatteryDevicePresent) {
        KLOG_WARNING(LOG_TAG, "No battery devices found\n");
        hc->periodic_chores_interval_fast = -1;
        hc->periodic_chores_interval_slow = -1;
    } else {
        if (mHealthdConfig->batteryStatusPath.isEmpty())
            KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
        if (mHealthdConfig->batteryHealthPath.isEmpty())
            KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
        if (mHealthdConfig->batteryPresentPath.isEmpty())
            KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
        if (mHealthdConfig->batteryCapacityPath.isEmpty())
            KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
        if (mHealthdConfig->batteryVoltagePath.isEmpty())
            KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
        if (mHealthdConfig->batteryTemperaturePath.isEmpty())
            KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
        if (mHealthdConfig->batteryTechnologyPath.isEmpty())
            KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
        if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
            KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
        if (mHealthdConfig->batteryFullChargePath.isEmpty())
            KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
        if (mHealthdConfig->batteryCycleCountPath.isEmpty())
            KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
        if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
            KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
        if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
            KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
        if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
            KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
    }

    if (property_get("ro.boot.fake_battery", pval, NULL) > 0
                                               && strtol(pval, NULL, 10) != 0) {
        mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;  //如果这里赋值了,那BatteryMonitor::updateValues就会使用这个值
        mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;  //如果这里赋值了,那BatteryMonitor::updateValues就会使用这个值
    }
}

}; // n

 /system/core/healthd/BatteryMonitor.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
    status_t ret = BAD_VALUE;
    std::string buf;

    val->valueInt64 = LONG_MIN;

    switch(id) {
    case BATTERY_PROP_CHARGE_COUNTER:
        if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
            val->valueInt64 =
                getIntField(mHealthdConfig->batteryChargeCounterPath);
            ret = OK;
        } else {
            ret = NAME_NOT_FOUND;
        }
        break;

    case BATTERY_PROP_CURRENT_NOW:
        if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
            val->valueInt64 =
                getIntField(mHealthdConfig->batteryCurrentNowPath);
            ret = OK;
        } else {
            ret = NAME_NOT_FOUND;
        }
        break;

    case BATTERY_PROP_CURRENT_AVG:
        if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
            val->valueInt64 =
                getIntField(mHealthdConfig->batteryCurrentAvgPath);
            ret = OK;
        } else {
            ret = NAME_NOT_FOUND;
        }
        break;

    case BATTERY_PROP_CAPACITY:
        if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
            val->valueInt64 =
                getIntField(mHealthdConfig->batteryCapacityPath);
            ret = OK;
        } else {
            ret = NAME_NOT_FOUND;
        }
        break;

    case BATTERY_PROP_ENERGY_COUNTER:
        if (mHealthdConfig->energyCounter) {
            ret = mHealthdConfig->energyCounter(&val->valueInt64);
        } else {
            ret = NAME_NOT_FOUND;
        }
        break;

    case BATTERY_PROP_BATTERY_STATUS:
        val->valueInt64 = getChargeStatus();
        ret = OK;
        break;

    default:
        break;
    }

    return ret;
}

=========================================================================

struct HealthInfo和struct BatteryProperties的定义如下:

后续可通过convertToHealthInfo()/convertFromHealthInfo()进行数据交换:

1.getProperty()获取数据保存在struct HealthInfo

2.convertFromHealthInfo()将数据拷贝到struct BatteryProperties

3.healthd_mode_ops->battery_update(&props);上报电池数据

 /frameworks/native/services/batteryservice/include/batteryservice/BatteryService.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

namespace android {

#include "BatteryServiceConstants.h"

// must be kept in sync with definitions in
// frameworks/base/core/java/android/os/BatteryManager.java
enum {
    BATTERY_PROP_CHARGE_COUNTER = 1, // equals BATTERY_PROPERTY_CHARGE_COUNTER
    BATTERY_PROP_CURRENT_NOW = 2, // equals BATTERY_PROPERTY_CURRENT_NOW
    BATTERY_PROP_CURRENT_AVG = 3, // equals BATTERY_PROPERTY_CURRENT_AVERAGE
    BATTERY_PROP_CAPACITY = 4, // equals BATTERY_PROPERTY_CAPACITY
    BATTERY_PROP_ENERGY_COUNTER = 5, // equals BATTERY_PROPERTY_ENERGY_COUNTER
    BATTERY_PROP_BATTERY_STATUS = 6, // equals BATTERY_PROPERTY_BATTERY_STATUS
};

struct BatteryProperties {
    bool chargerAcOnline;
    bool chargerUsbOnline;
    bool chargerWirelessOnline;
    int maxChargingCurrent;
    int maxChargingVoltage;
    int batteryStatus;
    int batteryHealth;
    bool batteryPresent;
    int batteryLevel;
    int batteryVoltage;
    int batteryTemperature;
    int batteryCurrent;
    int batteryCycleCount;
    int batteryFullCharge;
    int batteryChargeCounter;
    String8 batteryTechnology;
};

struct BatteryProperty {
    int64_t valueInt64;
};

}; // namespace android

 /hardware/interfaces/health/1.0/types.hal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

struct HealthInfo {
    /** AC charger state - 'true' if online */
    bool chargerAcOnline;

    /** USB charger state - 'true' if online */
    bool chargerUsbOnline;

    /** Wireless charger state - 'true' if online */
    bool chargerWirelessOnline;

    /** Maximum charging current supported by charger in uA */
    int32_t maxChargingCurrent;

    /** Maximum charging voltage supported by charger in uV */
    int32_t maxChargingVoltage;

    BatteryStatus batteryStatus;

    BatteryHealth batteryHealth;

    /** 'true' if battery is present */
    bool batteryPresent;

    /** Remaining battery capacity in percent */
    int32_t batteryLevel;

    /**
     * Instantaneous battery voltage in millivolts (mV).
     *
     * Historically, the unit of this field is microvolts (uV), but all
     * clients and implementations uses millivolts in practice, making it
     * the de-facto standard.
     */
    int32_t batteryVoltage;

    /** Instantaneous battery temperature in tenths of degree celcius */
    int32_t batteryTemperature;

    /** Instantaneous battery current in uA */
    int32_t batteryCurrent;

    /** Battery charge cycle count */
    int32_t batteryCycleCount;

    /** Battery charge value when it is considered to be "full" in uA-h */
    int32_t batteryFullCharge;

    /** Instantaneous battery capacity in uA-h */
    int32_t batteryChargeCounter;

    /** Battery technology, e.g. "Li-ion, Li-Poly" etc. */
    string batteryTechnology;
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值