如何从python代码中直接访问Android的Service
在Kivy中,通过pyjnius扩展可以间接调用Java代码,而pyjnius利用的是Java的反射机制。但是在Python对象和Java对象中转来转去总让人感觉到十分别扭。好在android提供了binder这个进程间通信的功能,Java中的Service也是基于Binder的C++代码封装来实现进程间通信的,这也为从Python代码中绕开pyjnius直接访问Java代码提供了可能,既然Java的Service是基于C++的封装来实现的,也同样可以在Python中封装同样的C++代码,这篇文章讲解了如何通过binder在Python代码中直接访问Java的Service,如WifiService。
binder_wrap.h
#ifndefBINDER_WRAP_H#defineBINDER_WRAP_H#ifdef__cplusplusextern"C"{#endiftypedefint(*vector_visitor)(constchar16_t*str16,intlength,void*data);typedefint(*fnOnTransact)(uint32_tcode,constvoid*data,void*reply,uint32_tflags,void*userData);intserver_create(constchar*name,constchar*descriptor,fnOnTransactonTrans,void*data);void*binder_getbinder(constchar*name);intbinder_releasebinder(void*binder);intbinder_listServices(vector_visitorvisitor,void*data);intbinder_getInterfaceDescriptor(void*binder,char16_t*descriptor,size_tsize);intbinder_transact(void*binder,intcode,constvoid*data,void*reply,intflags);void*parcel_new();intparcel_destroy(void*parcel);intparcel_writeInterfaceToken(void*parcel,constchar*interface);intparcel_writeInt32(void*parcel,intval);intparcel_writeCString(void*parcel,constchar*str);intparcel_writeString16(void*parcel,constchar16_t*str,size_tlen);intparcel_readInt32(void*parcel);longparcel_readInt64(void*parcel);intparcel_readString16(void*parcel,char16_t*str,size_tlen);intparcel_readInplace(void*parcel,void*data,intlen);intparcel_readExceptionCode(void*parcel);intparcel_dataAvail(void*parcel);#ifdef__cplusplus}#endif#endif
binder_wrap.cpp
#include<sys/types.h>#include<unistd.h>#include<grp.h>#include<binder/IPCThreadState.h>#include<binder/ProcessState.h>#include<binder/IServiceManager.h>#include<utils/Log.h>#include<binder/Parcel.h>#include"binder_wrap.h"usingnamespaceandroid;void*binder_getbinder(constchar*name){android::sp<android::IServiceManager>sm=android::defaultServiceManager();sp<IBinder>*binder=newsp<IBinder>();do{*binder=sm->getService(android::String16(name));if(binder!=0){break;}usleep(500000);//0.5s}while(true);returnreinterpret_cast<void*>(binder);}intbinder_releasebinder(void*binder){sp<IBinder>*bp=reinterpret_cast<sp<IBinder>*>(binder);if(bp==0){return0;}deletebp;return1;}//Vector<String16>listServices()=0;intbinder_listServices(vector_visitorvisitor,void*data){android::sp<android::IServiceManager>sm=android::defaultServiceManager();Vector<String16>list=sm->listServices();for(inti=0;i<list.size();i++){visitor(list[i].string(),list[i].size(),data);}returnlist.size();}intbinder_getInterfaceDescriptor(void*binder,char16_t*descriptor,size_tsize){sp<IBinder>*bp=reinterpret_cast<sp<IBinder>*>(binder);if(bp==0){return0;}if(descriptor==NULL||size<=0){return0;}String16des=(*bp)->getInterfaceDescriptor();if(size>des.size()){size=des.size();}memcpy(descriptor,des.string(),size*2);returnsize;}//intbinder_transact(void*binder,intcode,constParcel&data,Parcel*reply,intflags=0)intbinder_transact(void*binder,intcode,constvoid*data,void*reply,intflags){sp<IBinder>*bp=reinterpret_cast<sp<IBinder>*>(binder);if(bp==0||data==0||reply==0){return0;}return(*bp)->transact(code,*(Parcel*)data,(Parcel*)reply,flags);}void*parcel_new(){return(void*)newParcel();}intparcel_destroy(void*parcel){if(parcel==0){return0;}delete(Parcel*)parcel;return1;}intparcel_writeInterfaceToken(void*parcel,constchar*interface){Parcel*p=reinterpret_cast<Parcel*>(parcel);if(p==0){return0;}returnp->writeInterfaceToken(String16(interface));}intparcel_writeInt32(void*parcel,intval){Parcel*p=reinterpret_cast<Parcel*>(parcel);if(p==0){return0;}returnp->writeInt32(val);}intparcel_writeCString(void*parcel,constchar*str){Parcel*p=reinterpret_cast<Parcel*>(parcel);if(p==0){return0;}returnp->writeCString(str);}intparcel_writeString16(void*parcel,constchar16_t*str,size_tlen){Parcel*p=reinterpret_cast<Parcel*>(parcel);if(p==0){return0;}if(str==0||len<=0){return0;}returnp->writeString16(str,len);}intparcel_readInt32(void*parcel){Parcel*p=reinterpret_cast<Parcel*>(parcel);if(p==0){return0;}returnp->readInt32();}longparcel_readInt64(void*parcel){Parcel*p=reinterpret_cast<Parcel*>(parcel);if(p==0){return0;}returnp->readInt64();}intparcel_readString16(void*parcel,char16_t*str,size_tlen){Parcel*p=reinterpret_cast<Parcel*>(parcel);if(p==0){return0;}if(str==NULL||len<=0){return0;}String16str16=p->readString16();if(len>str16.size()){len=str16.size();}memcpy(str,str16.string(),len*2);returnlen;}intparcel_readExceptionCode(void*parcel){Parcel*p=reinterpret_cast<Parcel*>(parcel);if(p==0){return0;}returnp->readExceptionCode();}intparcel_readInplace(void*parcel,void*data,intlen){Parcel*p=reinterpret_cast<Parcel*>(parcel);if(p==0){return0;}if(len>=0&&len<=(int32_t)p->dataAvail()){constvoid*d=p->readInplace(len);memcpy(data,d,len);returnlen;}return0;}intparcel_dataAvail(void*parcel){Parcel*p=reinterpret_cast<Parcel*>(parcel);if(p==0){return0;}returnp->dataAvail();}
正如代码中所示,这里对C++的IBinder和Parcel两个对象进行了封装,而Java的Service的底层实现也正是对这两个类进行封装的结果,具体的可以看
frameworks\base\core\jni\android_util_Binder.cpp
的代码,
再来看下如何在Python中使用这些代码,这里用cython来封装这些C接口:
binder.pyx
cdefexternfrom"utils/Unicode.h":ctypedefshortchar16_tctypedefunsignedintuint32_tcdefexternfrom"Python.h":ctypedefshortPy_UNICODEctypedefsize_tPy_ssize_tobjectPyString_FromStringAndSize(constchar*v,Py_ssize_tlen)intPyString_AsStringAndSize(objectobj,char**buffer,Py_ssize_t*length)objectPyUnicode_FromUnicode(constPy_UNICODE*u,Py_ssize_tsize)Py_UNICODE*PyUnicode_AS_UNICODE(object)Py_ssize_tPyUnicode_GetSize(object)voidPy_INCREF(object)voidPy_DECREF(object)cdefexternfrom"binder_wrap.h":ctypedefint(*vector_visitor)(constchar16_t*str16,intlength,void*data)intbinder_listServices(vector_visitorvisitor,void*data)ctypedefint(*fnOnTransact)(uint32_tcode,constvoid*data,void*reply,uint32_tflags,void*userData)intserver_create(constchar*name,constchar*descriptor,fnOnTransactonTrans,void*data)void*binder_getbinder(constchar*name)intbinder_releasebinder(void*binder)intbinder_getInterfaceDescriptor(void*binder,char16_t*descriptor,intsize)intbinder_transact(void*binder,intcode,constvoid*data,void*reply,intflags)void*parcel_new()intparcel_destroy(void*parcel)intparcel_writeInterfaceToken(void*parcel,constchar*interface)intparcel_writeInt32(void*parcel,intval)intparcel_writeCString(void*parcel,constchar*str)intparcel_writeString16(void*parcel,constchar16_t*str,size_tlen)intparcel_readInt32(void*parcel)intparcel_readInt64(void*parcel)intparcel_readString16(void*parcel,char16_t*str,size_tlen)intparcel_readExceptionCode(void*parcel)intparcel_readInplace(void*parcel,void*data,intlen)intparcel_dataAvail(void*parcel)cdefintvisitor(constchar16_t*str16,intlength,void*data):arr=<object>datao=PyUnicode_FromUnicode(<Py_UNICODE*>str16,length)arr.append(o)deflistServices():arr=[]Py_INCREF(arr)binder_listServices(visitor,<void*>arr)Py_DECREF(arr)returnarrcdefclassBinder:cdefvoid*ptrdef__cinit__(self,char*name):#,sp[IBinder]service):self.ptr=binder_getbinder(name)def__dealloc__(self):binder_releasebinder(self.ptr)defgetInterfaceDescriptor(self):cdefchar16_tdescriptor[256]cdefintretret=binder_getInterfaceDescriptor(self.ptr,descriptor,sizeof(descriptor))ifnotret:returnNonereturnPyUnicode_FromUnicode(<Py_UNICODE*>descriptor,ret)deftransact(self,intcode,data,reply,intflags):cdefintdataPtr=data.getNativePtr()cdefintreplyPtr=reply.getNativePtr()binder_transact(self.ptr,code,<void*>dataPtr,<void*>replyPtr,flags)returnreplycdefclassParcel:cdefvoid*ptrcdefintnativePtrdef__cinit__(self,unsignedintnativePtr=0):#,sp[IBinder]service):self.nativePtr=nativePtrifnotnativePtr:self.ptr=parcel_new()else:self.ptr=<void*>nativePtrdef__dealloc__(self):ifnotself.nativePtr:parcel_destroy(self.ptr)defgetNativePtr(self):return<int>self.ptrdefwriteInterfaceToken(self,constchar*interface):returnparcel_writeInterfaceToken(<void*>self.ptr,interface)defwriteInt(self,intval):self.writeInt32(val)defwriteInt32(self,intval):returnparcel_writeInt32(<void*>self.ptr,val)defwriteCString(self,constchar*cstr):returnparcel_writeCString(<void*>self.ptr,cstr)defwriteString16(self,ustr):cdefchar16_t*uncdefintsizeifisinstance(ustr,unicode):un=<char16_t*>PyUnicode_AS_UNICODE(ustr)size=PyUnicode_GetSize(ustr)returnparcel_writeString16(<void*>self.ptr,un,size)defreadInt32(self):returnparcel_readInt32(self.ptr)defreadInt(self):returnself.readInt32()defreadInt64(self):returnparcel_readInt64(self.ptr)defreadExceptionCode(self):returnparcel_readExceptionCode(self.ptr)defreadString16(self):cdefchar16_tstr16[256]cdefintretret=parcel_readString16(self.ptr,str16,sizeof(str16))ifnotret:returnNonereturnPyUnicode_FromUnicode(<Py_UNICODE*>str16,ret)defreadByteArray(self):returnself.createByteArray()defcreateByteArray(self):length=self.readInt()print'createByteArray:',lengthreturnself.readInplace(length)#intparcel_readInplace(void*parcel,void*data,size_tlen)defreadInplace(self,length):cdefchararr[512]ret=parcel_readInplace(self.ptr,arr,length)ifret==length:returnPyString_FromStringAndSize(arr,length)else:returnNone#intparcel_dataAvail(void*parcel)defdataAvail(self):returnparcel_dataAvail(self.ptr)defcreateTypedArrayList(self,creator):N=self.readInt()ifN<=0:returnNonearr=[]foriinrange(N):ifself.readInt()==0:continueelse:result=creator.createFromParcel(self)arr.append(result)returnarr@classmethoddefobtain(cls):returnParcel()@classmethoddefrecycle(cls):pass
好,再来看看如何来实现访问WifiService的功能:
WifiService.py
frombinderimportBinder,ParcelWIFI_SERVICE="wifi";DESCRIPTOR="android.net.wifi.IWifiManager";FIRST_CALL_TRANSACTION=1TRANSACTION_getConfiguredNetworks=(FIRST_CALL_TRANSACTION+0);TRANSACTION_addOrUpdateNetwork=(FIRST_CALL_TRANSACTION+1);TRANSACTION_removeNetwork=(FIRST_CALL_TRANSACTION+2);TRANSACTION_enableNetwork=(FIRST_CALL_TRANSACTION+3);TRANSACTION_disableNetwork=(FIRST_CALL_TRANSACTION+4);TRANSACTION_pingSupplicant=(FIRST_CALL_TRANSACTION+5);TRANSACTION_startScan=(FIRST_CALL_TRANSACTION+6);TRANSACTION_getScanResults=(FIRST_CALL_TRANSACTION+7);TRANSACTION_disconnect=(FIRST_CALL_TRANSACTION+8);TRANSACTION_reconnect=(FIRST_CALL_TRANSACTION+9);TRANSACTION_reassociate=(FIRST_CALL_TRANSACTION+10);TRANSACTION_getConnectionInfo=(FIRST_CALL_TRANSACTION+11);TRANSACTION_setWifiEnabled=(FIRST_CALL_TRANSACTION+12);TRANSACTION_getWifiEnabledState=(FIRST_CALL_TRANSACTION+13);TRANSACTION_setCountryCode=(FIRST_CALL_TRANSACTION+14);TRANSACTION_setFrequencyBand=(FIRST_CALL_TRANSACTION+15);TRANSACTION_getFrequencyBand=(FIRST_CALL_TRANSACTION+16);TRANSACTION_isDualBandSupported=(FIRST_CALL_TRANSACTION+17);TRANSACTION_saveConfiguration=(FIRST_CALL_TRANSACTION+18);TRANSACTION_getDhcpInfo=(FIRST_CALL_TRANSACTION+19);TRANSACTION_acquireWifiLock=(FIRST_CALL_TRANSACTION+20);TRANSACTION_updateWifiLockWorkSource=(FIRST_CALL_TRANSACTION+21);TRANSACTION_releaseWifiLock=(FIRST_CALL_TRANSACTION+22);TRANSACTION_initializeMulticastFiltering=(FIRST_CALL_TRANSACTION+23);TRANSACTION_isMulticastEnabled=(FIRST_CALL_TRANSACTION+24);TRANSACTION_acquireMulticastLock=(FIRST_CALL_TRANSACTION+25);TRANSACTION_releaseMulticastLock=(FIRST_CALL_TRANSACTION+26);TRANSACTION_setWifiApEnabled=(FIRST_CALL_TRANSACTION+27);TRANSACTION_getWifiApEnabledState=(FIRST_CALL_TRANSACTION+28);TRANSACTION_getWifiApConfiguration=(FIRST_CALL_TRANSACTION+29);TRANSACTION_setWifiApConfiguration=(FIRST_CALL_TRANSACTION+30);TRANSACTION_startWifi=(FIRST_CALL_TRANSACTION+31);TRANSACTION_stopWifi=(FIRST_CALL_TRANSACTION+32);TRANSACTION_addToBlacklist=(FIRST_CALL_TRANSACTION+33);TRANSACTION_clearBlacklist=(FIRST_CALL_TRANSACTION+34);TRANSACTION_getWifiServiceMessenger=(FIRST_CALL_TRANSACTION+35);TRANSACTION_getWifiStateMachineMessenger=(FIRST_CALL_TRANSACTION+36);TRANSACTION_getConfigFile=(FIRST_CALL_TRANSACTION+37);TRANSACTION_captivePortalCheckComplete=(FIRST_CALL_TRANSACTION+38);mRemote=Binder(WIFI_SERVICE)deftransact(TRANSACTION):_data=Parcel()_reply=Parcel()_data.writeInterfaceToken(DESCRIPTOR)mRemote.transact(TRANSACTION,_data,_reply,0)_reply.readExceptionCode()return_reply.readInt32()defgetConfiguredNetworks():passdefaddOrUpdateNetwork():passdefremoveNetwork():passdefenableNetwork(netId,disableOthers):_data=Parcel()_reply=Parcel()_data.writeInterfaceToken(DESCRIPTOR)_data.writeInt32(netId)ifdisableOthers:_data.writeInt32(1)else:_data.writeInt32(0)mRemote.transact(TRANSACTION_enableNetwork,_data,_reply,0)_reply.readExceptionCode()return_reply.readInt32()!=0defdisableNetwork(netId):_data=Parcel()_reply=Parcel()_data.writeInterfaceToken(DESCRIPTOR)_data.writeInt32(netId)mRemote.transact(TRANSACTION_disableNetwork,_data,_reply,0)_reply.readExceptionCode()return_reply.readInt32()!=0defpingSupplicant():_data=Parcel()_reply=Parcel()_data.writeInterfaceToken(DESCRIPTOR)mRemote.transact(TRANSACTION_pingSupplicant,_data,_reply,0)_reply.readExceptionCode()return_reply.readInt32()!=0defstartScan(forceActive):_data=Parcel()_reply=Parcel()ret=0try:_data.writeInterfaceToken(DESCRIPTOR)ifforceActive:_data.writeInt(1)else:_data.writeInt(0)mRemote.transact(TRANSACTION_startScan,_data,_reply,0)ret=_reply.readExceptionCode()finally:_reply.recycle()_data.recycle()returnret==0classScanResult:def__init__(self,ssid,bssid,caps,level,frequency,timestamp):self.ssid=ssidself.bssid=bssidself.caps=capsself.level=levelself.frequency=frequencyself.timestamp=timestamp@classmethoddefcreateFromParcel(cls,reply):has_ssid=reply.readInt32()ssid=Noneifhas_ssid:ssid_lengt=reply.readInt()ssid=reply.readByteArray()BSSID=reply.readString16()caps=reply.readString16()level=reply.readInt()frequency=reply.readInt()timestamp=reply.readInt64()print'BSSID:',BSSIDprint'caps:',capsprint'level:',levelprint'frequency:',frequencyprint'timestamp:',timestampreturnScanResult(ssid,BSSID,caps,level,frequency,timestamp)defgetScanResults():_data=Parcel.obtain()_reply=Parcel.obtain()_result=Nonetry:_data.writeInterfaceToken(DESCRIPTOR)mRemote.transact(TRANSACTION_getScanResults,_data,_reply,0)if0!=_reply.readExceptionCode():returnNone_result=_reply.createTypedArrayList(ScanResult)finally:_reply.recycle()_data.recycle()return_resultdefdisconnect():returntransact(TRANSACTION_disconnect)!=0defreconnect():returntransact(TRANSACTION_reconnect)!=0defreassociate():returntransact(TRANSACTION_reassociate)!=0"""classWifiInfo:def__init__():pass@classmethoddefcreateFromParcel(cls,r):info=WifiInfo();info.networkId=r.readInt32()info.rssi=r.readInt32()info.linkSpeed=r.readInt32()ifr.readByte()==1:info.setInetAddress(InetAddress.getByAddress(in.createByteArray()))ifr.readInt()==1:info.mWifiSsid=WifiSsid.CREATOR.createFromParcel(r)info.mBSSID=r.readString16()info.mMacAddress=r.readString16()info.mMeteredHint=r.readInt32()!=0"""defgetConnectionInfo():passdefsetWifiEnabled(enable):_data=Parcel()_reply=Parcel()_data.writeInterfaceToken(DESCRIPTOR)ifenable:_data.writeInt32(1)else:_data.writeInt32(0)mRemote.transact(TRANSACTION_setWifiEnabled,_data,_reply,0)_reply.readExceptionCode()_result=(0!=_reply.readInt32())return_result;defgetWifiEnabledState():returntransact(TRANSACTION_getWifiEnabledState)defsetCountryCode(country,persist):_data=Parcel()_reply=Parcel()_data.writeInterfaceToken(DESCRIPTOR)ifisinstance(country,str):country=unicode(contry)_data.writeString16(country)ifpersist:_data.writeInt32(1)else:_data.writeInt32(0)mRemote.transact(TRANSACTION_setCountryCode,_data,_reply,0)_reply.readExceptionCode()_result=(0!=_reply.readInt32())return_result;defsetFrequencyBand(band,persist):_data=Parcel()_reply=Parcel()_data.writeInterfaceToken(DESCRIPTOR)ifisinstance(country,str):country=unicode(contry)_data.writeInt32(band)ifpersist:_data.writeInt32(1)else:_data.writeInt32(0)mRemote.transact(TRANSACTION_setFrequencyBand,_data,_reply,0)_reply.readExceptionCode()_result=(0!=_reply.readInt32())return_result;defgetFrequencyBand():returntransact(TRANSACTION_getFrequencyBand)defisDualBandSupported():returntransact(TRANSACTION_isDualBandSupported)!=0defsaveConfiguration():passdefget_readable_address(addr):return"%d:%d:%d:%d"%(addr&0xff,(addr>>8)&0xff,(addr>>16)&0xff,(addr>>24)&0xff)defgetDhcpInfo():_data=Parcel()_reply=Parcel()_data.writeInterfaceToken(DESCRIPTOR)mRemote.transact(TRANSACTION_getDhcpInfo,_data,_reply,0)_reply.readExceptionCode()if0==_reply.readInt32():returnNoneipAddress=get_readable_address(reply.readInt32());gateway=get_readable_address(reply.readInt32());netmask=get_readable_address(reply.readInt32());dns1=get_readable_address(reply.readInt32());dns2=get_readable_address(reply.readInt32());serverAddress=get_readable_address(reply.readInt32());leaseDuration=get_readable_address(reply.readInt32());info=(ipAddress,gateway,netmask,dns1,dns2,serverAddress,leaseDuration)print"ipAddress%s,\ngateway%s,\nnetmask%s,\ndns1%s,\ndns2%s,\nserverAddress%s,\nleaseDuration%s"%inforeturninfodefacquireWifiLock():passdefupdateWifiLockWorkSource():passdefreleaseWifiLock():passdefinitializeMulticastFiltering():passdefisMulticastEnabled():passdefacquireMulticastLock():passdefreleaseMulticastLock():passdefsetWifiApEnabled(wifiConfig,enable):_data=Parcel()_reply=Parcel()_data.writeInterfaceToken(DESCRIPTOR)ifwifiConfig:_data.writeInt32(1)wifiConfig.writeToParcel(_data)else:_data.writeInt32(0)ifenable:_data.writeInt32(1)else:_data.writeInt32(0)mRemote.transact(TRANSACTION_setWifiApEnabled,_data,_reply,0)_reply.readExceptionCode()defgetWifiApEnabledState():returntransact(TRANSACTION_getWifiApEnabledState)defgetWifiApConfiguration():passdefsetWifiApConfiguration():passdefstartWifi():returntransact(TRANSACTION_startWifi)defstopWifi():returntransact(TRANSACTION_stopWifi)defaddToBlacklist(bssid):_data=Parcel()_reply=Parcel()_data.writeInterfaceToken(DESCRIPTOR)ifisinstance(bssid,str):bssid=unicode(bssid)_data.writeString16(bssid)mRemote.transact(TRANSACTION_addToBlacklist,_data,_reply,0)_reply.readExceptionCode()defclearBlacklist():returntransact(TRANSACTION_clearBlacklist)defgetWifiServiceMessenger():passdefgetWifiStateMachineMessenger():passdefgetConfigFile():_data=Parcel()_reply=Parcel()_data.writeInterfaceToken(DESCRIPTOR)mRemote.transact(TRANSACTION_getConfigFile,_data,_reply,0)_reply.readExceptionCode()return_reply.readString16()defcaptivePortalCheckComplete():returntransact(TRANSACTION_captivePortalCheckComplete)!=0
目前并没有实现所有的WifiService的功能,但是像startScan,getScanResults,setWifiEnabled,getWifiEnabledState,getDhcpInfo,setWifiApEnabled这些主要的接口已经实现了,其它接口没有实现并非是因为不能实现,而是比较繁琐,暂时未实现而己,后面会不断的完善。
再来看下测试代码:
test.py
importWifiServiceWifiService.setWifiEnabled(True)WifiService.startScan(True)printWifiService.pingSupplicant()printWifiService.getConfigFile()foriinrange(10):time.sleep(1.0)result=WifiService.getScanResults()ifresult:printresultbreak
执行后将会打印出搜索到的Wifi信息。
另外就是代码的编译问题了。代码必须在android的源代码下进行编译。我试过在ndk上进行编译,经过一番努力,通过链接事先编译好的C++ binder库,也成功编译通过,但是程序不能正常运行,这应该是预先编译出来的库和ndk的库存在兼容性问题造成的,或许通过在ndk上编译binder库可以避免这个问题,但是目前还没有作过尝试。 但是编译出来的代码应该可以运行在各个不同的版本,我在4.0和4.2版本的设备上作了简单的测试,事实证明在4.2上编译的代码可以在4.0上运行,但是考虑到android的诸多版本,各个版本多多少少有些兼容性问题,更详细的还必须比较各个版本的binder代码,并通过测试才能得到结果。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。