赞
踩
这篇文章将所有的android蓝牙的东西都集合在这里,主要分一下几个部分,所有分析都基于Bluedroid:
(1) Android Bluedroid蓝牙基本框架
(2) 主要目录及文件结构
(3) 蓝牙主要的支持的profile
(4) 蓝牙基本流程分析:蓝牙启动, 蓝牙搜索, 蓝牙连接, 蓝牙传输文件,蓝牙通话和播放音乐
(1) Android Bluedroid蓝牙基本框架
bluetooth.jpg
根据上图,代码调用过程一般基于下面的流程
APP->Framework---->(通过Binder)BluetoothService-->(通过JNI)调用Native Bluetooth->bluedroid->hci
(2) 模块的主要目录以及文件结构:
APP:
Settings
- packages/apps/Settings/src/com/android/settings/bluetooth
- 主要文件说明:
- BluetoothEnabler.java 界面上蓝牙开启、关闭的开关就是它了,
- BluetoothSettings.java 主界面,用于管理配对和连接设备
- LocalBluetoothManager.java 提供了蓝牙API上的简单调用接口,这里只是开始。
- CachedBluetoothDevice.java 描述蓝牙设备的类,对BluetoothDevice的再封装
- BluetoothPairingDialog.java 那个配对提示的对话框
Phone
- packages/apps/services/Telecom/src/com/android/server/telecom/BluetoothManager.java
- 这里是通话调用蓝牙Audio,
- connectBluetoothAudio
- disconnectBluetoothAudio
Framework:
- /frameworks/base/core/java/android/bluetooth/
- BluetoothA2dp.java A2DP的功能实现
- BluetoothAdapter.java 蓝牙action的定义,虚拟设备属性以及操作方法
- BluetoothAudioGateway.java 蓝牙语音网关
- BluetoothClass.java 蓝牙设备类型的定义
- BluetoothDevice.java 蓝牙设备属性
- BluetoothDevicePicker.java 定义远程蓝牙设备的特性,比如需要认证,设备类型
- BluetoothHeadset.java 定义蓝牙headset功能的属性以及接口
- BluetoothInputStream.java 蓝牙流接口的实现(输入流)
- BluetoothOutputStream.java 蓝牙流接口的实现(输出流)
- BluetoothServerSocket.java 蓝牙socket服务端具备的方法
- BluetoothSocket.java 蓝牙socket的封装
- BluetoothUuid.java 蓝牙uuid的定义以及uuid的解析
(3) 蓝牙主要的支持的profile
HFP/HSP
A2DP
AVRCP
PBAP
DUN
OPP
PAN
(4) 蓝牙基本流程分析:
a. 蓝牙开启:
首先是BluetoothManagerService的启动, 这个服务也是在SystemServer中启动的, 在开机后,SystemServer进程由zygote进程fork出来后,会启动一系列的service,这里面就有BluetoothManagerService:
- frameworks/base/services/java/com/android/server/SystemServer.java
- private void startOtherServices() {
- BluetoothManagerService bluetooth = null;
- //初始化一个BluetoothManagerService;
- bluetooth = new BluetoothManagerService(context);
- ServiceManager.addService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, bluetooth);
- }
-
- 看一下BluetoothManagerService的构造函数
- BluetoothManagerService(Context context) {
- loadStoredNameAndAddress(); //读取蓝牙打开默认名称和地址
- if (isBluetoothPersistedStateOn()) { //判断蓝牙是否打开
- mEnableExternal = true; //如果蓝牙打开,这个设为true,等boot完后,开启enable 蓝牙的过程
- }
- }
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- synchronized(mReceiver) {
- if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
- //Enable
- if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
- sendEnableMsg(mQuietEnableExternal);
- }
- }
- }
- 广播中接收Boot是否完成, 完成后调用sendEnable来开启蓝牙,mEnableExternal这个值就在这里用到了。
b. 点击蓝牙开关开启蓝牙流程
Settings菜单点击菜单开启蓝牙:
APP层路径packages/apps/Settings/src/com/android/settings/bluetooth
BluetoothSettings.java
- public void onActivityCreated(Bundle savedInstanceState) {
- mSwitchBar = activity.getSwitchBar(); //界面上的swtichbar控件
- mBluetoothEnabler = new BluetoothEnabler(activity, mSwitchBar); //传送给BluetoothEnabler, 通过BluetoothEnable控制
- mBluetoothEnabler.setupSwitchBar();
- }
BluetoothEnabler.java
- private final LocalBluetoothAdapter mLocalAdapter; //localBluetoothAdapter
- public BlutoothEnable(){ //构造函数,定义了localBluetoothManager和LocalBluetoothAdapter
- LocalBluetoothManager manager = LocalBluetoothManager.getInstance(context);
- mLocalAdapter = manager.getBluetoothAdapter();
- }
- public void resume(Context context) {
- // Bluetooth state is not sticky, so set it manually
- handleStateChanged(mLocalAdapter.getBluetoothState()); //设置初始状态
- mSwitchBar.addOnSwitchChangeListener(this); //设置switch监听
- }
- public void onSwitchChanged(Switch switchView, boolean isChecked) {
- if (mLocalAdapter != null) {
- mLocalAdapter.setBluetoothEnabled(isChecked); //
- }
- }
LocalBluetoothAdapter.java
- public void setBluetoothEnabled(boolean enabled) {
- private final BluetoothAdapter mAdapter;
- boolean success = enabled? mAdapter.enable(): mAdapter.disable(); 调用BluetoothAdapter的enable/disable
- }
通过调用BluetoothAdapter的函数,就调用到了framework的接口:
LocalBluetoothAdapter.java
- private final IBluetoothManager mManagerService;
- public static synchronized BluetoothAdapter getDefaultAdapter() {
- IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b); //这边会返回一个managerService的代理
- }
- public boolean enable() {
- return mManagerService.enable(ActivityThread.currentPackageName()); //通过代理调用BluetoothManagerService的enable.
- }
BluetoothManagerService
- class BluetoothManagerService extends IBluetoothManager.Stub{
- private final BluetoothHandler mHandler;
- private IBluetooth mBluetooth;
- public boolean enable(String callingPackage) {
- //这里会获取蓝牙的权限
- synchronized(mReceiver) {
- //通过发送消息的方式enable
- sendEnableMsg(false);
- }
- }
-
- private void sendEnableMsg(boolean quietMode) {
- //这边回去发送消息, quietMode决定打开后是否需要AutoConnect,这边是false
- mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
- quietMode ? 1 : 0, 0));
- }
- //Handler是BluetoothManagerService的内部类,这边处理接收的命令
- private class BluetoothHandler extends Handler {
- public void handleMessage(Message msg) {
- case MESSAGE_ENABLE:
- //调用handleEnable函数处理
- handleEnable(msg.arg1 == 1);
- break;
- case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
- mBluetooth = IBluetooth.Stub.asInterface(service);
- break;
- }
- }
-
- private void handleEnable(boolean quietMode) {
- synchronized(mConnection) {
- try {
- //传进来是false,所以走enable,即开启会重新连接
- if (!mQuietEnable) {
- //调用mBluetooth的enable,mBluetooth上面有定义,是IBluetooth的代理, server端在package/apps/Bluetooth里面
- if(!mBluetooth.enable()) {
- }
- }
- else {
- if(!mBluetooth.enableNoAutoConnect()) {
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG,"Unable to call enable()",e);
- }
- }
- }
- }
之前调用IBluetooth的service端的部分:
AdapterService.java
- static { //这里添加了so库,并定义了一些 native函数
- System.load("/system/lib/libbluetooth_jni.so");
- classInitNative();
- }
- private static class AdapterServiceBinder extends IBluetooth.Stub {
- public boolean enable() {
- return service.enable(); //这边又要看这个service,这个service即是 AdapterService
- }
- }
-
- boolean enable() {
- return enable (false);
- }
- public synchronized boolean enable(boolean quietMode) {
- //这边会发送一个消息到状态机AdapaterState
- Message m =
- mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON);
- mAdapterStateMachine.sendMessage(m);
- }
-
- void processStart() {
- //启动蓝牙的部分, 如果是第一次开启的话, 走下面的路径, 继续想state状态机发送AdapterState.STARTED状态
- if (!mProfilesStarted && supportedProfileServices.length >0) {
- //Startup all profile services
- setProfileServiceState(supportedProfileServices,BluetoothAdapter.STATE_ON);
- }else {
- debugLog("processStart() - Profile Services alreay started");
- mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.STARTED));
- }
- }
-
-
AdapterState.java 状态机文件,负责跟AdapterService的沟通
- private class OffState extends State {
- public boolean processMessage(Message msg) {
- switch(msg.what) {
- case USER_TURN_ON:
- adapterService.processStart(); //adpaterService调用processStart函数
- break;
- }
- }
最后,看一下状态机里面的函数
- private class PendingCommandState extends State {
- public boolean processMessage(Message msg) {
- boolean ret = adapterService.enableNative(); //这边就调用到jni的地方了
- }
看一下简单的流程图, 大体一致,但也稍微有不同的地方
Bluetooth_on.jpg
这边只到了JNI,下面JNI如何调用CPP的呢~
这边调用packages/apps/Bluetooth/jni下面的jni文件:
com_android_bluetooth_btservice_AdapterService.cpp
- static jboolean enableNative(JNIENv* env, jobject obj){
- int ret = sBluetoothInterface->enable();
- }
在然后好像真的没有了, 后面进入到hal,调用Bluedroid协议栈了
hardware/libhardware/include/hardware/bluetooth.h
- typedef struct{
- int(*enable)(void);
- }bt_interface_t;
Bluedroid协议栈位于external/bluetooth/bluedroid
bluetooth.c
- static int enable(void){
- return btif_enable_bluetooth();
- }
btif_core.c
- bt_status_t btif_enable_bluetooth(void)
- {
- bte_main_enable();
- }
bte_main.c
void bte_main_enable(){
BTE_Init();
GKI_create_task(); //创建GKI task
bte_hci_enable();
GKI_run();
}
c. 蓝牙搜索:
APP测:
BluetoothSettings.java
- //蓝牙状态改变监听
- public void onBluetoothStateChanged(int bluetoothState) {
- super.onBluetoothStateChanged(bluetoothState);
- updateContent(bluetoothState);
- }
-
- private void updateContent(int bluetoothState) {
- case BluetoothAdapter.STATE_ON: //蓝牙开启完成后,开始扫描
- if (!mInitialScanStarted) {
- startScanning();
- }
- break;
- case BluetoothAdapter.STATE_TURNING_ON: //蓝牙开启中,将scanStart设置为false
- mInitialScanStarted = false;
- break;
- }
- 这边的BluetoothAdapter.STATE有4个状态:
- STATE_OF = 10;
- STATE_TURNING_ON = 11;
- STATE_ON = 12;
- STATE_TURNING_OFF = 13;
-
- private void startScanning() {
- mLocalAdapter.startScanning(true);//调用localAdapter的startScanning函数
- }
LocalBluetoothAdapter.java
- void startScanning(boolean force) {
- // If we are playing music, don't scan unless forced.
- //这里回去判断a2dp profile,如果正在a2dp存在,则不能扫描
- A2dpProfile a2dp = mProfileManager.getA2dpProfile();
- if (a2dp != null && a2dp.isA2dpPlaying()) {
- return;
- }
- A2dpSinkProfile a2dpSink = mProfileManager.getA2dpSinkProfile();
- if ((a2dpSink != null) && (a2dpSink.isA2dpPlaying())){
- return;
- }
- if (mAdapter.startDiscovery()) { //调用framwork的接口
- mLastScan = System.currentTimeMillis();
- }
- }
Framework:
BluetoothAdapter.java
- private IBluetooth mService; //这么快就用到IBluetooth了。。。
- BluetoothAdapter(IBluetoothManager managerService) {
- try {
- //构造函数里直接就获取mService,这个mService跟managerService是一样一样的,是IBluetooth的客户端代理,跟Service(AdapterService)通讯
- mService = managerService.registerAdapter(mManagerCallback);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- }
-
- public boolean startDiscovery() {
- if (mService != null) return mService.startDiscovery(); //开始扫描
- }
还是来看一下mService是如何获取的
BluetoothManagerService.java
- public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
- synchronized(mConnection) {
- return mBluetooth; //就是返回BluetoothManagerService的mBluetooth
- }
- }
Service (Service的目录在packages/apps/bluetooth里面)
AdapterService.java
- private static class AdapterServiceBinder extends IBluetooth.Stub {
- public boolean startDiscovery() {
- AdapterService service = getService();
- return service.startDiscovery();
- }
- }
- 这里有个内部类AdapterServiceBinder,是远程通讯的服务端:
- 直接调用了AdapterService的startDiscovery函数
- boolean startDiscovery() {
- enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH ADMIN permission");
- //do not allow new connections with active multicast
- A2dpService a2dpService = A2dpService.getA2dpService();
- if (a2dpService != null &&
- a2dpService.isMulticastOngoing(null)) {
- Log.i(TAG,"A2dp Multicast is Ongoing, ignore discovery");
- return false;
- }
- return startDiscoveryNative();
- }
- 这边也是简单粗暴的,直接返回startDiscoveryNative(),调用JNI函数
JNI
JNI的文件需要到packages/apps/bluetooth/jni下面,adapterservice对应的jni文件为
com_android_bluetooth_btservice_AdapterService.cpp
- static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) {
- int ret = sBluetoothInterface->start_discovery();
- }
HAL
hal主要看hanrdware/libhardware/include/hardware/bluetooth.h
int (*start_discovery)(void);
//Bluedroid协议栈部分
bluetooth.c
- static int statrt_discovery(void){
- retun btif_dm_start_discovery();
- }
btif_dm.c
- 这个函数start device discovery/inquiry
- bt_status_t btif_dm_start_discovery(void){
- /*find nearby devices*/
- BTA_DmSearch(...);
- }
d. 蓝牙扫描结果返回
扫描结果反馈到上层则是从协议栈->hal->bluetoothService->APP
external/bluetooth/bluedroid/btif/src/
btif_dm.c
- static void btif_dm_search_devices_evt (UINT16 event, char *p_param)
- {
- case BTA_DM_INQ_RES_EVT:
- /* Callback to notify upper layer of device */
- //调用注册的callback, device_found_cb
- HAL_CBACK(bt_hal_cbacks, device_found_cb,num_properties, properties);
- break;
- }
HAL
bluetooth.h
- //
- typedef void (*device_found_callback)(int num_properties,
- bt_property_t *properties);
JNI
com_android_bluetooth_btservice_AdapterService.cpp
- static void device_found_callback(int num_properties, bt_property_t *properties) {
- if (sJniCallbacksObj) {
- callbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceFoundCallback, addr);
- }
- }
Service
- //deviceFoundCallback
- RemoteDevices.java
- void deviceFoundCallback(byte[] address) {
- Intent intent = new Intent(BluetoothDevice.ACTION_FOUND); //send broadcast
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothDevice.EXTRA_CLASS,
- new BluetoothClass(Integer.valueOf(deviceProp.mBluetoothClass)));
- intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
- intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
-
- mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
- }
- 这里调用发送广播,Action_Found给到上层
APP(Settings)
BluetoothEventManager.java
- BluetoothEventManager(LocalBluetoothAdapter adapter,
- CachedBluetoothDeviceManager deviceManager, Context context) {
- mLocalAdapter = adapter;
- mDeviceManager = deviceManager;
- mAdapterIntentFilter = new IntentFilter();
- mProfileIntentFilter = new IntentFilter();
- mHandlerMap = new HashMap<String, Handler>();
- mContext = context;
-
- // Bluetooth on/off broadcasts
- addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());
-
- // Discovery broadcasts
- addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
- addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
- addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
- addHandler(BluetoothDevice.ACTION_DISAPPEARED, new DeviceDisappearedHandler());
- addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());
-
- // Pairing broadcasts
- addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedHandler());
- addHandler(BluetoothDevice.ACTION_PAIRING_CANCEL, new PairingCancelHandler());
-
- // Fine-grained state broadcasts
- addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler());
- addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());
-
- // Dock event broadcasts
- addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
-
- mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter);
-
- setDefaultBtName();
- }
- 这边注册广播接收器
- addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
-
- private class DeviceFoundHandler implements Handler {
- public void onReceive(Context context, Intent intent,
- BluetoothDevice device) {
- if (cachedDevice == null) {
- cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
- Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
- + cachedDevice);
- // callback to UI to create Preference for new device
- dispatchDeviceAdded(cachedDevice);
- }
- }
- }
- DeviceFoundHandler接收广播,并调用dispatchDeviceAdded(cachedDevice);刷新上层显示
-
- private void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onDeviceAdded(cachedDevice);
- }
- }
- }
- 调用callback.onDeviceAdded(cachedDevice); 回调返给上层
ListFragment DeviceListPreferenceFragment.java
- public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
- 、、、
- }
- DeviceListPreferenceFragment继承BluetoothCallback接口,重写onDeviceAdded.
蓝牙连接:
蓝牙OPP文件传输:
起点自函数packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
- if (action.equals(Intent.ACTION_SEND) || action.equals(Intent.ACTION_SEND_MULTIPLE)) {
- if (!isBluetoothAllowed()) {
- Intent in = new Intent(this, BluetoothOppBtErrorActivity.class);
- in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- in.putExtra("title", this.getString(R.string.airplane_error_title));
- in.putExtra("content", this.getString(R.string.airplane_error_msg));
- startActivity(in);
- finish();
- return;
- }
- if (action.equals(Intent.ACTION_SEND)) {
- Thread t = new Thread(new Runnable() {
- public void run() {
- BluetoothOppManager.getInstance(BluetoothOppLauncherActivity.this)
- .saveSendingFileInfo(type,stream.toString(), false);
- //Done getting file info..Launch device picker and finish this activity
- launchDevicePicker();
- finish();
- }
- });
- t.start();
- return;
- }
- }
- isBluetoothAllowed()判断Bluetooth是否允许,如果不允许,弹出Error.
- 如果返回true,则开启个线程,
- launchDevicePicker 加载蓝牙列表
-
- private final void launchDevicePicker() {
- if (!BluetoothOppManager.getInstance(this).isEnabled()) {
- if (V) Log.v(TAG, "Prepare Enable BT!! ");
- Intent in = new Intent(this, BluetoothOppBtEnableActivity.class);
- in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(in);
- } else {
- if (V) Log.v(TAG, "BT already enabled!! ");
- Intent in1 = new Intent(BluetoothDevicePicker.ACTION_LAUNCH);
- in1.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- in1.putExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false);
- in1.putExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
- BluetoothDevicePicker.FILTER_TYPE_TRANSFER);
- in1.putExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE,
- Constants.THIS_PACKAGE_NAME);
- in1.putExtra(BluetoothDevicePicker.EXTRA_LAUNCH_CLASS,
- BluetoothOppReceiver.class.getName());
- if (V) {Log.d(TAG,"Launching " +BluetoothDevicePicker.ACTION_LAUNCH );}
- startActivity(in1);
- }
- }
- BluetoothOppManager.getInstance(this).isEnabled()判断蓝牙是否打开,如果打开,则跳转到列表
DevicePickerFragment.java
- @Override
- void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
- mLocalAdapter.stopScanning();
- LocalBluetoothPreferences.persistSelectedDeviceInPicker(
- getActivity(), mSelectedDevice.getAddress());
- if ((btPreference.getCachedDevice().getBondState() ==
- BluetoothDevice.BOND_BONDED) || !mNeedAuth) {
- sendDevicePickedIntent(mSelectedDevice);
- finish();
- } else {
- super.onDevicePreferenceClick(btPreference);
- }
- }
- private void sendDevicePickedIntent(BluetoothDevice device) {
- mDeviceSelected = true;
- Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- if (mLaunchPackage != null && mLaunchClass != null) {
- intent.setClassName(mLaunchPackage, mLaunchClass);
- }
- getActivity().sendBroadcast(intent);
- }
- 这边发送一个selectd的广播,搜关键字查询广播接收。
BluetoothOppReceiver.java
- public void onReceive(Context context, Intent intent) {
- else if (action.equals(BluetoothDevicePicker.ACTION_DEVICE_SELECTED)) {
- // Insert transfer session record to database
- mOppManager.startTransfer(remoteDevice);
- }
- }
-
-
- public void startTransfer(BluetoothDevice device) {
- if (V) Log.v(TAG, "Active InsertShareThread number is : " + mInsertShareThreadNum);
- InsertShareInfoThread insertThread;
-
- insertThread = new InsertShareInfoThread(device, mMultipleFlag, mMimeTypeOfSendingFile,
- mUriOfSendingFile, mNameOfSendingFile, mMimeTypeOfSendingFiles, mUrisOfSendingFiles,
- mIsHandoverInitiated);
-
- }
-
- insertThread.start();
- }
- private class InsertShareInfoThread extends Thread {
- public void run() {
- insertSingleShare();
- }
- }
- 上面步骤即是接收广播后的操作~最终调用insertSingleShare来操作,后面的步骤略掉。
蓝牙接电话与听音乐:
作者:唐僧不爱洗头_f7b5
链接:https://www.jianshu.com/p/b68b281f9020
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。