当前位置:   article > 正文

OpenHarmony js到hal层通信机制_openharmony hal

openharmony hal

1. JS 到 Native JS ---- NAPI 通信

1.1WIFI的native js目录结构
├── js
│   └── napi 	// js 和 c++ 通信
│       ├── inc
│       └── src
└── native
    ├── c_adapter
    │   ├── inc
    │   └── src
    ├── include
    ├── interfaces
    └── src
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
1.2 js 与 c++方法绑定
communication\wifi\interfaces\innerkits\native_cpp\napi\wifi_napi_entry.cpp
    
namespace OHOS {
namespace Wifi {
#ifndef ENABLE_NAPI_COMPATIBLE
/*
 * Module initialization function
 */
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("enableWifi", EnableWifi),  //
        DECLARE_NAPI_FUNCTION("disableWifi", DisableWifi),
        DECLARE_NAPI_FUNCTION("isWifiActive", IsWifiActive),
        // ... 绑定函数 
        
    };
	// 将desc中设置到export中
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(napi_property_descriptor), desc));
    return exports;
}

// 1. 创建module
static napi_module wifiJsModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = NULL,
    .nm_register_func = Init, // 2. 注册函数表
    .nm_modname = "wifi",	// module name
    .nm_priv = ((void *)0),
    .reserved = { 0 }
};

    // 3. 注册module 
extern "C" __attribute__((constructor)) void RegisterModule(void) {
    napi_module_register(&wifiJsModule);

}
}  // namespace Wifi
}  // namespace OHOS
  • 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
napi_value EnableWifi(napi_env env, napi_callback_info info)
{
    TRACE_FUNC_CALL;
    NAPI_ASSERT(env, wifiDevicePtr != nullptr, "Wifi device instance is null.");
    ErrCode ret = wifiDevicePtr->EnableWifi();
    WriteWifiStateHiSysEvent(HISYS_SERVICE_TYPE_STA, WifiOperType::ENABLE);
    napi_value result;
    napi_get_boolean(env, ret == WIFI_OPT_SUCCESS, &result);
    return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
1.3 .d.ts文件声明
/**
 * Provides methods to operate or manage Wi-Fi.
 *
 * @since 6
 * @import import wifi from '@ohos.wifi';
 */
declare namespace wifi {
    function enableWifi(): boolean;
    function disableWifi(): boolean;
    function isWifiActive(): boolean;
    function scan(): boolean;
    function getScanInfos(): Promise<Array<WifiScanInfo>>;
    // ...
}

export default wifi;

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

NAPI接口参考文档

OpenHarmony 源码解析之JavaScript API框架(NAPI)-开源基础软件社区-51CTO.COM

2. Native——FWK IPC通信-----Remote()

#过年不停更# Openharmony IPC通信(L2)-开源基础软件社区-51CTO.COM

2.1 Binder通信

Remote() 封装的 binder 通信

AndroidOHOS
SeviceManager管理SystemAbilityManagerClient管理
service自己注册到binder需要手动注册到binder
只需要关注方法和参数,其余aidl自动生成需要自己手写逻辑

binder aidl 生成文件
    	// 客户端发请求    
		@Override
        public void addPerson(Person person) throws RemoteException {
        	Parcel data = Parcel.obtain();
        	Parcel reply = Parcel.obtain();
        	try {
            	data.writeInterfaceToken(DESCRIPTOR);// 1. 写入binder标识
            	if (null != person) {
                	data.writeInt(1);
                	person.writeToParcel(data, 0); // 2. 写入数据
            	} else {
                	data.writeInt(0);
            	}
            	mRemote.transact(TRANSACTION_addPerson, data, reply, 0);// 3. 发消息
            	reply.readException();
        	} finally {
            	data.recycle();
            	reply.recycle();
        	}
    	}
		// 服务端接收请求
		protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION:
                    reply.writeString(DESCRIPTOR);
                    return true;
                case TRANSACTION_addPerson:
                    data.enforceInterface(DESCRIPTOR);
                    Person arg0;
                    if (0 != data.readInt()) {
                        arg0 = (Person) Person.CREATOR.createFromParcel(data);
                    } else {
                        arg0 = null;
                    }
                    this.addPerson(arg0);
                    reply.writeNoException();
                    return true;
                case TRANSACTION_getPersonList:
                    data.enforceInterface(DESCRIPTOR);
                    List<Person> result = getPersonList();
                    reply.writeNoException();
                    reply.writeTypedList(result);
                    return true;
            }
            return super.onTransact(code, data, reply, flags);
        }
  • 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
Remote 写法
// 发送请求
ErrCode WifiDeviceProxy::EnableWifi()
{
    if (mRemoteDied) {
        WIFI_LOGE("failed to `%{public}s`,remote service is died!", __func__);
        return WIFI_OPT_FAILED;
    }
    MessageOption option;
    MessageParcel data;
    MessageParcel reply;
    
    //1. 写入binder标识
    // DECLARE_INTERFACE_DESCRIPTOR(u"ohos.wifi.IWifiDeviceService");
    if (!data.WriteInterfaceToken(GetDescriptor())) {
        WIFI_LOGE("Write interface token error: %{public}s", __func__);
        return WIFI_OPT_FAILED;
    }
    //2. 写入数据
    data.WriteInt32(0);
    //3. 向服务端发消息 WIFI_SVR_CMD_ENABLE_WIFI
    int error = Remote()->SendRequest(WIFI_SVR_CMD_ENABLE_WIFI, data, reply, option);
    if (error != ERR_NONE) {
        WIFI_LOGE("Set Attr(%{public}d) failed,error code is %{public}d", WIFI_SVR_CMD_ENABLE_WIFI, error);
        return WIFI_OPT_FAILED;
    }

    int exception = reply.ReadInt32();
    if (exception) {
        return WIFI_OPT_FAILED;
    }
    return ErrCode(reply.ReadInt32());
}

// 处理请求
int WifiDeviceStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
    if (data.ReadInterfaceToken() != GetDescriptor()) {
        WIFI_LOGE("Sta stub token verification error");
        return WIFI_OPT_FAILED;
    }

    int exception = data.ReadInt32();
    if (exception) {
        return WIFI_OPT_FAILED;
    }

    HandleFuncMap::iterator iter = handleFuncMap.find(code);
    if (iter == handleFuncMap.end()) {
        WIFI_LOGI("not find function to deal, code %{public}u", code);
        reply.WriteInt32(0);
        reply.WriteInt32(WIFI_OPT_NOT_SUPPORTED);
    } else {
        (this->*(iter->second))(code, data, reply);
    }

    return 0;
}
  • 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

2.2 服务端注册

注册方式1

基本上自己有main函数入口:

​ xxxService继承IPCObjectStub 和 xxxInterface

	auto saMgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
	sptr<IRemoteObject> newInstance = new xxxService();
	int result = saMgr->AddSystemAbility(xxxSaId, newInstance);
  • 1
  • 2
  • 3
注册方式2

本身没有main函数入口,需要借助safwk的main入口:

xxxService继承IPCObjectStub,xxxInterface 和 SystemAbility

// 注册方式2的第一种方式
	REGISTER_SYSTEM_ABILITY_BY_ID(xxxService, xxxSaId, true); // 添加ability信息
	Publish(this)// 在重写的OnStart函数中调用,注册到samgr中
// 注册方式2的第二种方式
	const bool REGISTER_RESULT = SystemAbility::MakeAndRegisterAbility(WifiDeviceServiceImpl::GetInstance().GetRefPtr());
	Publish(this)

        

// WifiDeviceService 注册
const bool REGISTER_RESULT = SystemAbility::MakeAndRegisterAbility(WifiDeviceServiceImpl::GetInstance().GetRefPtr());

WifiDeviceServiceImpl::WifiDeviceServiceImpl()
#ifdef OHOS_ARCH_LITE
    : mPublishFlag(false), mState(ServiceRunningState::STATE_NOT_START)

#else
    : SystemAbility(WIFI_DEVICE_ABILITY_ID, true), mPublishFlag(false), mState(ServiceRunningState::STATE_NOT_START)
#endif
{}

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

2.3 客户端类定义

xxxServiceProxy继承PeerHolder 和 xxxInterface
客户端类中定义一个静态变量:

// 定义静态变量,才能通过iface_cast创建一个包含IPCObjectProxy的xxxServiceProxy对象
static inline BrokerDelegator<xxxServiceProxy> delegator_;
  • 1
  • 2

2.4 客户端获取注册信息

auto saMgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
sptr<IRemoteObject> object = saMgr->GetSystemAbility(SaId);
client = iface_cast<xxxInterface >(object);
  • 1
  • 2
  • 3
// WIFI Device范例
std::unique_ptr<OHOS::Wifi::WifiDevice> wifiDevicePtr = OHOS::Wifi::WifiDevice::GetInstance(WIFI_DEVICE_ABILITY_ID);

bool WifiDeviceImpl::Init()
{
    sptr<ISystemAbilityManager> sa_mgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    sptr<IRemoteObject> object = sa_mgr->GetSystemAbility(systemAbilityId_);
    client_ = iface_cast<IWifiDevice>(object);
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
// 服务id配置
distributedschedule\samgr\interfaces\innerkits\samgr_proxy\include\system_ability_definition.h

enum {
	// ...
    WIFI_DEVICE_SYS_ABILITY_ID                       = 1120,
    WIFI_HOTSPOT_SYS_ABILITY_ID                      = 1121,
    WIFI_ENHANCER_SYS_ABILITY_ID                     = 1122,
    WIFI_P2P_SYS_ABILITY_ID                          = 1123,
    // ... 
}

static const std::map<int, std::string> saNameMap_ = {
    { 200, "AccountMgr" },
    { 301, "AIEngine" },
    { BUNDLE_MGR_SERVICE_SYS_ABILITY_ID, "BundleMgr" },
    { FORM_MGR_SERVICE_ID, "FormMgr" },
    { WIFI_DEVICE_SYS_ABILITY_ID, "WifiDevice" },
    { WIFI_HOTSPOT_SYS_ABILITY_ID, "WifiHotspot" },
    { WIFI_ENHANCER_SYS_ABILITY_ID, "WifiEnhancer" },
    { WIFI_P2P_SYS_ABILITY_ID, "WifiP2p" },
	// ...	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

3. FWK——HAL Socket 通信

Linux Socket编程(不限Linux) - 吴秦 - 博客园 (cnblogs.com)

使用一个.sock为后缀的文件来进行进程间通信,写入就是发送请求,读取就是接受该请求的返回信息.

Server端

wifi/wifi/services/wifi_standard/wifi_hal/main.c
int main(void)
{
    LOGI("Wifi hal service starting...");
    char rpcSockPath[] = "/data/misc/wifi/unix_sock.sock";
    if (access(rpcSockPath, 0) == 0) {
        unlink(rpcSockPath);
    }
    if (InitRpcFunc() < 0) {  // 一、初始化函数表
        LOGE("Init Rpc Function failed!");
        return -1;
    }
    RpcServer *server = CreateRpcServer(rpcSockPath); // 二、建立Socket通信
    if (server == NULL) {
        LOGE("Create RPC Server by %{public}s failed!", rpcSockPath);
        return -1;
    }
    SetRpcServerInited(server);
    setvbuf(stdout, NULL, _IOLBF, 0);
    signal(SIGINT, SignalExit);
    signal(SIGTERM, SignalExit);
    signal(SIGPIPE, SIG_IGN);

    SendStartNotify();
    RunRpcLoop(server); // 三、进入事件循环
    /* stop wpa_supplicant, hostapd, and other resources */
    ForceStop();
    for (int id = 0; id < AP_MAX_INSTANCE; id++) {
        StopSoftAp(id);
    }
    P2pForceStop();
    ReleaseWifiHalVendorInterface();
    /* clear RPC Server */
    SetRpcServerInited(NULL);
    ReleaseRpcServer(server);
    ReleaseRpcFunc();
    LOGI("hal service exists!");
    return 0;
}


二、建立Socket通信
RpcServer *CreateRpcServer(const char *path)
{
    RpcServer *server = (RpcServer *)calloc(1, sizeof(RpcServer));
    int flag = 1;
    do {
        int ret = CreateUnixServer(path, DEFAULT_LISTEN_QUEUE_SIZE); // CreateUnixServer,建立socket通信
        server->listenFd = ret;
        server->loop = CreateEventLoop(MAX_SUPPORT_CLIENT_FD_SIZE); // 创建loop监听事件
        // ...
    } while (0);
    if (flag) {
        ReleaseRpcServer(server);
        return NULL;
    }
    return server;
}

int CreateUnixServer(const char *path, int backlog)
{
    struct sockaddr_un sockAddr;
    if (memset_s(&sockAddr, sizeof(sockAddr), 0, sizeof(sockAddr)) != EOK) {
        return -1;
    }
    sockAddr.sun_family = AF_LOCAL;
    if (strncpy_s(sockAddr.sun_path, sizeof(sockAddr.sun_path), path, strlen(path)) != EOK) {
        return -1;
    }
    int sock = CreateSocket(AF_LOCAL); // 创建socket int sock = socket(domain, SOCK_STREAM, 0);
    if (sock < 0) {
        return -1;
    }
    int keepAlive = 1;
    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
    int reuseaddr = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&reuseaddr, sizeof(reuseaddr));
    
    // 绑定"/data/misc/wifi/unix_sock.sock"并进行监听 1.bind 2. listen
    if (bind(sock, (struct sockaddr *)&, sizeof(sockAddr)) < 0) {  
    }
    if (SetNonBlock(sock, 1) != 0) {
    }
    fcntl(sock, F_SETFD, FD_CLOEXEC);
    if (listen(sock, backlog) < 0) {
    }
    return sock;
}

三、进行事件循环
int RunRpcLoop(RpcServer *server)
{
    if (server == NULL) {
        return -1;
    }
    EventLoop *loop = server->loop;
    while (!loop->stop) {
        BeforeLoop(server);
        int retval = epoll_wait(loop->epfd, loop->epEvents, loop->setSize, 5); // wait 5ms
        for (int i = 0; i < retval; ++i) {
            struct epoll_event *e = loop->epEvents + i;
            int fd = e->data.fd;
            unsigned int mask = CheckEventMask(e);
            if (fd == server->listenFd) {
                OnAccept(server, mask); 	// 接收事件:    int fd = accept(server->listenFd, NULL, NULL);
            } else {
                DealFdEvents(server, fd, mask);
            }
        }
    }
    return 0;
}
  • 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

总结:

1.int socket(int domain, int type, int protocol); 创建socket

2.int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 绑定

3.int listen(int sockfd, int backlog); 监听

4.int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 接收请求

Client 端

int WifiIdlClient::InitClient(void)
{
    const std::string idlSockPath = "/data/misc/wifi/unix_sock.sock"; // .sock文件
    pRpcClient = CreateRpcClient(idlSockPath.c_str());  // call CreateRpcClient
    // ...
}

RpcClient *CreateRpcClient(const char *path)
{
    int fd = ConnectUnixServer(path); // 连接socket
    SetNonBlock(fd, 1);
    RpcClient *client = (RpcClient *)calloc(1, sizeof(RpcClient));
	// ...
    client->context = CreateContext(CONTEXT_BUFFER_MIN_SIZE);
    // ...
    int ret = pthread_create(&client->threadId, NULL, RpcClientThreadDeal, client);
	// ...
    signal(SIGPIPE, SIG_IGN);
    return client;
}

int ConnectUnixServer(const char *path)
{
	// ...
    
    sockAddr.sun_family = AF_LOCAL;
    if (strncpy_s(sockAddr.sun_path, sizeof(sockAddr.sun_path), path, strlen(path)) != EOK) {
        return -1;
    }
    // 1.创建socket
    int sock = CreateSocket(AF_LOCAL); // int sock = socket(domain, SOCK_STREAM, 0);
    if (sock < 0) {
        return -1;
    }
    // 2.链接socket
    if (connect(sock, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) < 0) {
        LOGE("connect failed!");
        close(sock);
        return -1;
    }
    return sock;
}
  • 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
  1. int socket(int domain, int type, int protocol);
  2. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

client发消息到Server

WifiErrorNo Start(void)
{
    RpcClient *client = GetStaRpcClient();
    LockRpcClient(client);
    Context *context = client->context;
    WriteBegin(context, 0);
    WriteFunc(context, "Start"); // 写入context->szWrite
    WriteEnd(context);
    if (RpcClientCall(client, "Start") != WIFI_IDL_OPT_OK) { // 发送事件到Server
        return WIFI_IDL_OPT_FAILED;
    }
    int result = WIFI_IDL_OPT_FAILED;
    ReadInt(context, &result);
    ReadClientEnd(client);
    UnlockRpcClient(client);
    return result;
}

int RemoteCall(RpcClient *client)
{
	// ... 
    Context *context = client->context;
    while (context->wBegin != context->wEnd && ret >= 0) {
        ret = ContextWriteNet(context);
    }
    if (ret < 0) {
        return ret;
    }
    ret = 0; /* reset ret value */
    pthread_mutex_lock(&client->mutex);
    while (client->waitReply != CLIENT_STATE_DEAL_REPLY && client->waitReply != CLIENT_STATE_EXIT) {
        pthread_cond_wait(&client->condW, &client->mutex);
    }
    if (client->waitReply == CLIENT_STATE_EXIT) {
        ret = -1;
    }
    pthread_mutex_unlock(&client->mutex);
    return ret;
}

int ContextWriteNet(Context *context)
{

    if (context->wBegin < context->wEnd) {
        int ret = MyWrite(context->fd, context->szWrite + context->wBegin, context->wEnd - context->wBegin);// 写入事件int ret = write(fd, buf + pos, count);
        if (ret > 0) {
            context->wBegin += ret;
        }
        return ret;
    }
	// ...
    return ret;
}

  • 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

Server端接收

int RpcStart(RpcServer *server, Context *context)
{
    if (server == NULL || context == NULL) {
        return HAL_FAILURE;
    }
    WifiErrorNo err = Start();
    WriteBegin(context, 0);
    WriteInt(context, err);
    WriteEnd(context);
    return HAL_SUCCESS;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
// socket error  
LOGE("%s: error on socket(): %d = %s", __func__, errno, strerror(errno));
  • 1
  • 2
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/337606
推荐阅读
相关标签
  

闽ICP备14008679号