WifiP2pSettings工作流程

本文详细介绍了Android平台上P2P操作的具体流程,包括如何通过WifiP2pSettings进行设备搜索、连接以及断开连接等操作。文章还深入探讨了相关广播事件的处理过程,如WIFI_P2P_STATE_CHANGED_ACTION等。
Android平台中,P2P操作用户只需执行如下三个步骤:
1)进入WifiP2pSettings界面;
2)搜索周围的P2P设备。搜索到的设备将显示在WifiP2pSettings中。
3)用户选择其中的某个设备以发起连接。
首先来看WifiP2pSettings的onActivityCreate函数。
1.WifiP2pSettings创建

Android-5.1/packages/apps/Settings/src/com/android/settings/wifi/p2p/WifiP2pSettings.Java

  1.    public void onActivityCreated(Bundle savedInstanceState) {  
  2.        addPreferencesFromResource(R.xml.wifi_p2p_settings);//加载界面元素  
  3. /*WifiP2pSettings也是通过监听广播的方式来了解系统中Wi-Fi P2P相关的信息及变化情况。下面这几个广播属于P2P特有的,其作用如下:  
  4. WIFI_P2P_STATE_CHANGED_ACTION:用于通知系统中P2P功能的启用情况,如该功能是enable还是disable。  
  5. WIFI_P2P_PEERS_CHANGED_ACTION:系统内部将保存搜索到的其他P2P设备信息,如果这些信息有变化,则系统将发送该广播。接收者需要通过WifiP2PManager的requestPeers函数重新获取这些P2P设备的信息。  
  6. WIFI_P2P_CONNECTION_CHANGED_ACTION:用于通知P2P连接情况,该广播可携带WifiP2pInfo和NetworkInfo两个对象。相关信息可从这两个对象中获取。  
  7. WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:用于通知本机P2P设备信息发生了变化。  
  8. WIFI_P2P_DISCOVERY_CHANGED_ACTION:用于通知P2P Device Discovery的工作状态,如启动或停止。  
  9. WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION:用于通知persistent group信息发生了变化。  
  10.        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);  
  11.        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);  
  12.        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);  
  13.        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);  
  14.        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);  
  15.        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);  
  16.   
  17.        final Activity activity = getActivity();  
  18.     //创建WifiP2pManager对象,它将和WifiP2pService交互  
  19.        mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);  
  20.        if (mWifiP2pManager != null) {  
  21.         //初始化WifiManager并建立和WiFiService的联系  
  22.            mChannel = mWifiP2pManager.initialize(activity, getActivity().getMainLooper(), null);  
  23.            if (mChannel == null) {  
  24.                //Failure to set up connection  
  25.                Log.e(TAG, "Failed to set up connection with wifi p2p service");  
  26.                mWifiP2pManager = null;  
  27.            }  
  28.        } else {  
  29.            Log.e(TAG, "mWifiP2pManager is null !");  
  30.        }  
  31.   
  32.        if (savedInstanceState != null && savedInstanceState.containsKey(SAVE_DIALOG_PEER)) {  
  33.            WifiP2pDevice device = savedInstanceState.getParcelable(SAVE_DIALOG_PEER);  
  34.            mSelectedWifiPeer = new WifiP2pPeer(getActivity(), device);  
  35.        }  
  36.        if (savedInstanceState != null && savedInstanceState.containsKey(SAVE_DEVICE_NAME)) {  
  37.            mSavedDeviceName = savedInstanceState.getString(SAVE_DEVICE_NAME);  
  38.        }  
  39.        if (savedInstanceState != null && savedInstanceState.containsKey(SAVE_SELECTED_GROUP)) {  
  40.            mSelectedGroupName = savedInstanceState.getString(SAVE_SELECTED_GROUP);  
  41.        }  
  42.   
  43.     //创建UI中按钮对应的onClickListener  
  44.        mRenameListener = new OnClickListener() {  
  45.            @Override  
  46.            public void onClick(DialogInterface dialog, int which) {  
  47.                if (which == DialogInterface.BUTTON_POSITIVE) {  
  48.                    if (mWifiP2pManager != null) {  
  49.                        mWifiP2pManager.setDeviceName(mChannel,  
  50.                                mDeviceNameText.getText().toString(),  
  51.                                new WifiP2pManager.ActionListener() {  
  52.                            public void onSuccess() {  
  53.                                if (DBG) Log.d(TAG, " device rename success");  
  54.                            }  
  55.                            public void onFailure(int reason) {  
  56.                                Toast.makeText(getActivity(),  
  57.                                        R.string.wifi_p2p_failed_rename_message,  
  58.                                        Toast.LENGTH_LONG).show();  
  59.                            }  
  60.                        });  
  61.                    }  
  62.                }  
  63.            }  
  64.        };  
  65.   
  66.        //disconnect dialog listener  
  67.        mDisconnectListener = new OnClickListener() {  
  68.            @Override  
  69.            public void onClick(DialogInterface dialog, int which) {  
  70.                if (which == DialogInterface.BUTTON_POSITIVE) {  
  71.                    if (mWifiP2pManager != null) {  
  72.                        mWifiP2pManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {  
  73.                            public void onSuccess() {  
  74.                                if (DBG) Log.d(TAG, " remove group success");  
  75.                            }  
  76.                            public void onFailure(int reason) {  
  77.                                if (DBG) Log.d(TAG, " remove group fail " + reason);  
  78.                            }  
  79.                        });  
  80.                    }  
  81.                }  
  82.            }  
  83.        };  
  84.   
  85.        //cancel connect dialog listener  
  86.        mCancelConnectListener = new OnClickListener() {  
  87.            @Override  
  88.            public void onClick(DialogInterface dialog, int which) {  
  89.                if (which == DialogInterface.BUTTON_POSITIVE) {  
  90.                    if (mWifiP2pManager != null) {  
  91.                        mWifiP2pManager.cancelConnect(mChannel,  
  92.                                new WifiP2pManager.ActionListener() {  
  93.                            public void onSuccess() {  
  94.                                if (DBG) Log.d(TAG, " cancel connect success");  
  95.                            }  
  96.                            public void onFailure(int reason) {  
  97.                                if (DBG) Log.d(TAG, " cancel connect fail " + reason);  
  98.                            }  
  99.                        });  
  100.                    }  
  101.                }  
  102.            }  
  103.        };  
  104.   
  105.        //delete persistent group dialog listener  
  106.        mDeleteGroupListener = new OnClickListener() {  
  107.            @Override  
  108.            public void onClick(DialogInterface dialog, int which) {  
  109.                if (which == DialogInterface.BUTTON_POSITIVE) {  
  110.                    if (mWifiP2pManager != null) {  
  111.                        if (mSelectedGroup != null) {  
  112.                            if (DBG) Log.d(TAG, " deleting group " + mSelectedGroup.getGroupName());  
  113.                            mWifiP2pManager.deletePersistentGroup(mChannel,  
  114.                                    mSelectedGroup.getNetworkId(),  
  115.                                    new WifiP2pManager.ActionListener() {  
  116.                                public void onSuccess() {  
  117.                                    if (DBG) Log.d(TAG, " delete group success");  
  118.                                }  
  119.                                public void onFailure(int reason) {  
  120.                                    if (DBG) Log.d(TAG, " delete group fail " + reason);  
  121.                                }  
  122.                            });  
  123.                            mSelectedGroup = null;  
  124.                        } else {  
  125.                            if (DBG) Log.w(TAG, " No selected group to delete!" );  
  126.                        }  
  127.                    }  
  128.                } else if (which == DialogInterface.BUTTON_NEGATIVE) {  
  129.                    if (DBG) {  
  130.                        Log.d(TAG, " forgetting selected group " + mSelectedGroup.getGroupName());  
  131.                    }  
  132.                    mSelectedGroup = null;  
  133.                }  
  134.            }  
  135.        };  
  136.   
  137.        setHasOptionsMenu(true);  
  138.   
  139.        final PreferenceScreen preferenceScreen = getPreferenceScreen();  
  140.        preferenceScreen.removeAll();  
  141.        preferenceScreen.setOrderingAsAdded(true);  
  142.   
  143.        mThisDevicePref = new Preference(getActivity());  
  144.        mThisDevicePref.setPersistent(false);  
  145.        mThisDevicePref.setSelectable(false);  
  146.        preferenceScreen.addPreference(mThisDevicePref);  
  147.   
  148.        mPeersGroup = new PreferenceCategory(getActivity());  
  149.        mPeersGroup.setTitle(R.string.wifi_p2p_peer_devices);  
  150.        preferenceScreen.addPreference(mPeersGroup);  
  151.   
  152.        mPersistentGroup = new PreferenceCategory(getActivity());  
  153.        mPersistentGroup.setTitle(R.string.wifi_p2p_remembered_groups);  
  154.        preferenceScreen.addPreference(mPersistentGroup);  
  155.   
  156.        super.onActivityCreated(savedInstanceState);  
  157.    }  

2.WifiP2pSettings工作流程
1)WIFI_P2P_STATE_CHANGED_ACTION处理流程
打开WifiP2pSettings后,首先要等待 WIFI_P2P_STATE_CHANGED_ACTION广播以判断P2P功能是否正常启动。相应的处理函数如下所示:
android-5.1/packages/apps/Settings/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
  1. private final BroadcastReceiver mReceiver = new BroadcastReceiver() {  
  2.     @Override  
  3.     public void onReceive(Context context, Intent intent) {  
  4.         String action = intent.getAction();  
  5.   
  6.         if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {  
  7. //从 WIFI_P2P_STATE_CHANGED_ACTION广播中获取相关状态信息以判断P2P功能是否打开  
  8.             mWifiP2pEnabled = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,  
  9.                 WifiP2pManager.WIFI_P2P_STATE_DISABLED) == WifiP2pManager.WIFI_P2P_STATE_ENABLED;  
  10.             handleP2pStateChanged();  
  11. /如果搜索到新的P2P设备,则WIFI_P2P_PEERS_CHANGED_ACTION将被发送  
  12.         } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {  
  13.             mPeers = (WifiP2pDeviceList) intent.getParcelableExtra(  
  14.                     WifiP2pManager.EXTRA_P2P_DEVICE_LIST);  
  15.             handlePeersChanged();  
  16.         } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {  
  17.             if (mWifiP2pManager == null) return;  
  18.             NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(  
  19.                     WifiP2pManager.EXTRA_NETWORK_INFO);  
  20.             WifiP2pInfo wifip2pinfo = (WifiP2pInfo) intent.getParcelableExtra(  
  21.                     WifiP2pManager.EXTRA_WIFI_P2P_INFO);  
  22.             if (networkInfo.isConnected()) {  
  23.                 if (DBG) Log.d(TAG, "Connected");  
  24.             } else if (mLastGroupFormed != true) {  
  25.                 //start a search when we are disconnected  
  26.                 //but not on group removed broadcast event  
  27.                 startSearch();  //如果没有加入某个P2P组,则重新发起设备扫描  
  28.             }  
  29.             mLastGroupFormed = wifip2pinfo.groupFormed;  
  30.         } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {  
  31.             mThisDevice = (WifiP2pDevice) intent.getParcelableExtra(  
  32.                     WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);  
  33.             if (DBG) Log.d(TAG, "Update device info: " + mThisDevice);  
  34.             updateDevicePref();  
  35.         } else if (WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)) {  
  36.             int discoveryState = intent.getIntExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE,  
  37.                 WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);  
  38.             if (DBG) Log.d(TAG, "Discovery state changed: " + discoveryState);  
  39.             if (discoveryState == WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED) {  
  40.                 updateSearchMenu(true);//更新SEARCH按钮显示的名称  
  41.             } else {  
  42.                 updateSearchMenu(false);  
  43.             }  
  44.         } else if (WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION.equals(action)) {  
  45.             if (mWifiP2pManager != null) {  
  46.                 mWifiP2pManager.requestPersistentGroupInfo(mChannel, WifiP2pSettings.this);  
  47.             }  
  48.         }  
  49.     }  
  50. };  

  1. private void handleP2pStateChanged() {  
  2.     updateSearchMenu(false);//该函数将触发WifiP2pSettings的onCreateOptionsMenu被调用  
  3.     mThisDevicePref.setEnabled(mWifiP2pEnabled);  
  4.     mPeersGroup.setEnabled(mWifiP2pEnabled);  
  5.     mPersistentGroup.setEnabled(mWifiP2pEnabled);  
  6. }  
用户下一步要做的事情就是主动搜索周围的P2P设备。Android原生代码中的WifiP2pSettings界面下方有两个按钮,分别是SEARCH和RENAME,分别用于搜索周围的P2P Device和更改本机的P2P设备名。
当P2P功能正常启用后(即上述代码中的mWifiP2pEnabled为true时),这两个按钮将被使能。此后,用户就可单击SEARCH按钮以搜索周围的P2P设备。该按钮对应的函数是startSearch。
  1.  private void startSearch() {  
  2.      if (mWifiP2pManager != null && !mWifiP2pSearching) {  
  3. //discoverPeers将搜索周围的P2P设备  
  4.          mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {  
  5.              public void onSuccess() {  
  6.              }  
  7.              public void onFailure(int reason) {  
  8.                  if (DBG) Log.d(TAG, " discover fail " + reason);  
  9.              }  
  10.          });  
  11.      }  
  12.  }  
当WPAS完成搜索后, WIFI_P2P_PEERS_CHANGED_ACTION广播将被发送。对该广播的处理参考onReceive函数。startSearch还将触发系统发送 WIFI_P2P_DISCOVERY_CHANGED_ACTION广播,WifiP2pSettings将根据该广播携带的信息来更新SEARCH按钮的界面:如果P2P Discovery启动成功(即状态变为 WIFI_P2P_DISCOVERY_STARTED),则SEARCH按钮名称显示为Searching,否则该按钮显示为Search For Devices。
当系统搜索到新的P2P设备后,WIFI_P2P_PEERS_CHANGED_ACTION广播将被发送,而WifiP2pSettings对于该广播的处理就是调用WifiP2pManager的requestPeers来获取系统保存的P2P设备信息列表
系统中所有的P2P设备信息将通过PeerListener接口类的onPeersAvailable函数返回给WifiP2pSettings:
  1.   public void onPeersAvailable(WifiP2pDeviceList peers) {  
  2.       if (DBG) Log.d(TAG, "Requested peers are available");  
  3. //系统中所有的P2P设备信息都保存在这个类型为WifiP2pDeviceList的peers对象中  
  4.       mPeers = peers;  
  5.       handlePeersChanged();  
  6.   }  

  1.  private void handlePeersChanged() {  
  2.      mPeersGroup.removeAll();//mPeersGroup类型为PreferenceGroup,属于UI相关的类  
  3.   
  4.      mConnectedDevices = 0;  
  5.      if (DBG) Log.d(TAG, "List of available peers");  
  6.      for (WifiP2pDevice peer: mPeers.getDeviceList()) {  
  7.          if (DBG) Log.d(TAG, "-> " + peer);  
  8. //WifiP2pPeer是Preference的子类,它和UI相关  
  9.          mPeersGroup.addPreference(new WifiP2pPeer(getActivity(), peer));  
  10.          if (peer.status == WifiP2pDevice.CONNECTED) mConnectedDevices++;  
  11.      }  
  12.      if (DBG) Log.d(TAG, " mConnectedDevices " + mConnectedDevices);  
  13.  }  
在该函数中,WifiP2pDeviceList中保存的每一个WifiP2pDevice信息将作为一个Preference项添加到mPeersGroup中并显示在UI界面上。
接下来用户就可在界面中选择某个P2P设备并与之连接,这个步骤由onPreferenceTreeClick函数来完成。
  1. public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {  
  2.     if (preference instanceof WifiP2pPeer) {  
  3.         mSelectedWifiPeer = (WifiP2pPeer) preference;//获取用户指定的那一个WifiP2pPeer项  
  4.         if (mSelectedWifiPeer.device.status == WifiP2pDevice.CONNECTED) {  
  5.             showDialog(DIALOG_DISCONNECT);//如果已经和该设备连接,则判断是否需要与之断开连接  
  6.         } else if (mSelectedWifiPeer.device.status == WifiP2pDevice.INVITED) {  
  7.             showDialog(DIALOG_CANCEL_CONNECT);  
  8.         } else {//向对端P2P设备发起连接  
  9.             WifiP2pConfig config = new WifiP2pConfig();  
  10.             config.deviceAddress = mSelectedWifiPeer.device.deviceAddress;  
  11.   
  12. //判断系统是否强制使用了某种WSC配置方法  
  13.             int forceWps = SystemProperties.getInt("wifidirect.wps", -1);  
  14.   
  15.             if (forceWps != -1) {  
  16.                 config.wps.setup = forceWps;  
  17.             } else {  
  18.     //获取对端P2P设备支持的WSC配置方法,优先考虑PBC  
  19.                 if (mSelectedWifiPeer.device.wpsPbcSupported()) {  
  20.                     config.wps.setup = WpsInfo.PBC;  
  21.                 } else if (mSelectedWifiPeer.device.wpsKeypadSupported()) {  
  22.                     config.wps.setup = WpsInfo.KEYPAD;  
  23.                 } else {  
  24.                     config.wps.setup = WpsInfo.DISPLAY;  
  25.                 }  
  26.             }  
  27.   
  28. //通过WifiP2pManager的connect函数向对端P2P设备发起连接。注意,目标设备信息保存在config对象中  
  29.             mWifiP2pManager.connect(mChannel, config,  
  30.                     new WifiP2pManager.ActionListener() {  
  31.                         public void onSuccess() {  
  32.                             if (DBG) Log.d(TAG, " connect success");  
  33.                         }  
  34.                         public void onFailure(int reason) {  
  35.                             Log.e(TAG, " connect fail " + reason);  
  36.                             Toast.makeText(getActivity(),  
  37.                                     R.string.wifi_p2p_failed_connect_message,  
  38.                                     Toast.LENGTH_SHORT).show();  
  39.                         }  
  40.                 });  
  41.         }  
  42.     } else if (preference instanceof WifiP2pPersistentGroup) {  
  43.         mSelectedGroup = (WifiP2pPersistentGroup) preference;  
  44.         showDialog(DIALOG_DELETE_GROUP);  
  45.     }  
  46.     return super.onPreferenceTreeClick(screen, preference);  
  47. }  

当系统完成和P2P设备的连接后,WifiP2pSettings将收到 WIFI_P2P_CONNECTION_CHANGED_ACTION 广播,对应的代码见onReceive

<think> 我们需要分析wifip2psettings.java接收的状态变化发送方。首先,根据文件名,这似乎是Android系统中与Wi-Fi Direct(P2P)设置相关的Java类。 在Android开发中,Wi-Fi Direct(P2P)相关的状态变化通常来自于系统底层(通过WifiP2pManager)和广播接收器(BroadcastReceiver)。 回顾Android Wi-Fi P2P编程模型: 1. 应用程序通过WifiP2pManager与Wi-Fi Direct服务交互。 2. 状态变化(如连接状态、设备发现、对等设备列表变化等)通过广播(Broadcast)的形式发送。 3. 应用需要注册广播接收器来接收这些状态变化。 因此,wifip2psettings.java很可能是一个设置界面(Activity或Fragment),它通过注册广播接收器来接收系统发送的Wi-Fi P2P相关状态变化的广播。 具体来说,状态变化的发送方是Android系统(具体是WifiP2pService或相关系统服务)。当Wi-Fi P2P的状态发生变化时(如连接断开、发现新设备等),系统会发送对应的广播。 常见的广播包括: - WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION:Wi-Fi P2P开关状态变化 - WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION:对等设备列表变化 - WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION:连接状态变化 - WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:本设备信息变化 所以,wifip2psettings.java接收的状态变化发送方是Android系统(通过广播)。 此外,也可能包括用户操作界面(如按钮点击)引发的状态变化,但用户操作通常是在本地处理,不通过广播。广播主要是系统层面的事件。 因此,我们可以总结为:wifip2psettings.java主要接收来自Android系统发送的广播事件(由系统服务发出)来获取Wi-Fi P2P状态变化。 </think> 在 Android 系统中,`WifiP2pSettings.java` 接收的状态变化主要来自以下发送方: ### 1. **系统框架层** 系统底层通过 `WifiP2pService` 发送全局状态变更通知: - Wi-Fi P2P 开关状态变化(启用/禁用) - 设备发现状态变化(开始/停止搜索) - 连接状态变更(连接建立/断开) - 对等设备列表更新 这些事件通过广播机制(如 `WIFI_P2P_STATE_CHANGED_ACTION`)发送[^1]。 ### 2. **用户交互层** 通过 UI 操作触发的状态更新: - 用户点击「搜索设备」按钮发送设备扫描请求 - 用户选择设备发起连接时发送连接请求 - 取消连接/断开当前连接操作 这些交互通过 `WifiP2pManager` API 触发状态变更[^2]。 ### 3. **底层驱动通知** 来自 Wi-Fi 芯片驱动层的硬件事件: - 物理连接状态变化(如设备超出范围) - 数据传输状态变更 - 认证/加密协商结果 - 组网状态更新(群组创建/解散) ### 典型处理流程 ```java // 注册广播接收器 IntentFilter filter = new IntentFilter(); filter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); filter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); registerReceiver(mReceiver, filter); // 接收系统广播 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1); handleP2pStateChange(state); // 处理状态变化 } } }; ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值