这一章主要总结从WifiP2pService的启动到用户通过四种连接方式连接P2P的过程,四种方式包括:主动连接、被动连接、主动invite和被动invite。首先来看WifiP2pService的启动。
wifiP2p = new WifiP2pService(context); ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p); wifiP2p.connectivityServiceReady();
public WifiP2pService(Context context) { mContext = context; //STOPSHIP: get this from native side mInterface = "p2p0"; mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, ""); mP2pSupported = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_DIRECT); mThisDevice.primaryDeviceType = mContext.getResources().getString( com.android.internal.R.string.config_wifi_p2p_device_type); mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported); mP2pStateMachine.start(); }
P2pStateMachine(String name, boolean p2pSupported) { super(name); addState(mDefaultState); addState(mP2pNotSupportedState, mDefaultState); addState(mP2pDisablingState, mDefaultState); addState(mP2pDisabledState, mDefaultState); addState(mP2pEnablingState, mDefaultState); addState(mP2pEnabledState, mDefaultState); addState(mInactiveState, mP2pEnabledState); addState(mGroupCreatingState, mP2pEnabledState); addState(mUserAuthorizingInviteRequestState, mGroupCreatingState); addState(mUserAuthorizingNegotiationRequestState, mGroupCreatingState); addState(mProvisionDiscoveryState, mGroupCreatingState); addState(mGroupNegotiationState, mGroupCreatingState); addState(mFrequencyConflictState, mGroupCreatingState); addState(mGroupCreatedState, mP2pEnabledState); addState(mUserAuthorizingJoinState, mGroupCreatedState); addState(mOngoingGroupRemovalState, mGroupCreatedState); if (p2pSupported) { setInitialState(mP2pDisabledState); } else { setInitialState(mP2pNotSupportedState); } setLogRecSize(50); setLogOnlyTransitions(true); }
class P2pDisabledState extends State { @Override public void enter() { if (DBG) logd(getName()); } @Override public boolean processMessage(Message message) { if (DBG) logd(getName() + message.toString()); switch (message.what) { case WifiStateMachine.CMD_ENABLE_P2P: try { mNwService.setInterfaceUp(mInterface); } catch (RemoteException re) { loge("Unable to change interface settings: " + re); } catch (IllegalStateException ie) { loge("Unable to change interface settings: " + ie); } mWifiMonitor.startMonitoring(); transitionTo(mP2pEnablingState); break;
class P2pEnablingState extends State { @Override public void enter() { if (DBG) logd(getName()); } @Override public boolean processMessage(Message message) { if (DBG) logd(getName() + message.toString()); switch (message.what) { case WifiMonitor.SUP_CONNECTION_EVENT: if (DBG) logd("P2p socket connection successful"); transitionTo(mInactiveState); break; case WifiMonitor.SUP_DISCONNECTION_EVENT: loge("P2p socket connection failed"); transitionTo(mP2pDisabledState); break; case WifiStateMachine.CMD_ENABLE_P2P: case WifiStateMachine.CMD_DISABLE_P2P_REQ: deferMessage(message); break; default: return NOT_HANDLED; } return HANDLED; } }
class P2pEnabledState extends State { @Override public void enter() { if (DBG) logd(getName()); sendP2pStateChangedBroadcast(true); mNetworkInfo.setIsAvailable(true); sendP2pConnectionChangedBroadcast(); initializeP2pSettings(); }
initialize(Context, Looper, WifiP2pManager.ChannelListener)
before doing any p2p operation.
public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) { Messenger messenger = getMessenger(); if (messenger == null) return null; Channel c = new Channel(srcContext, srcLooper, listener); if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger) == AsyncChannel.STATUS_SUCCESSFUL) { return c; } else { return null; } }
public void discoverPeers(Channel c, ActionListener listener) { checkChannel(c); c.mAsyncChannel.sendMessage(DISCOVER_PEERS, 0, c.putListener(listener)); }
case WifiP2pManager.DISCOVER_PEERS: if (mDiscoveryBlocked) { replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, WifiP2pManager.BUSY); break; } // do not send service discovery request while normal find operation. clearSupplicantServiceRequest(); if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) { replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED); sendP2pDiscoveryChangedBroadcast(true); } else { replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, WifiP2pManager.ERROR); } break;
case WifiMonitor.P2P_DEVICE_FOUND_EVENT: WifiP2pDevice device = (WifiP2pDevice) message.obj; if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break; mPeers.updateSupplicantDetails(device); sendPeersChangedBroadcast(); break;
public void connect(Channel c, WifiP2pConfig config, ActionListener listener) { checkChannel(c); checkP2pConfig(config); c.mAsyncChannel.sendMessage(CONNECT, 0, c.putListener(listener), config); }
case WifiP2pManager.CONNECT: if (DBG) logd(getName() + " sending connect"); WifiP2pConfig config = (WifiP2pConfig) message.obj; if (isConfigInvalid(config)) { loge("Dropping connect requeset " + config); replyToMessage(message, WifiP2pManager.CONNECT_FAILED); break; } mAutonomousGroup = false; mWifiNative.p2pStopFind(); if (reinvokePersistentGroup(config)) { transitionTo(mGroupNegotiationState); } else { transitionTo(mProvisionDiscoveryState); } mSavedPeerConfig = config; mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED); sendPeersChangedBroadcast(); replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED); break;
class GroupCreatingState extends State { @Override public void enter() { if (DBG) logd(getName()); sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT, ++mGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS); } class ProvisionDiscoveryState extends State { @Override public void enter() { if (DBG) logd(getName()); mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig); }
case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT: provDisc = (WifiP2pProvDiscEvent) message.obj; device = provDisc.device; if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break; if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) { if (DBG) logd("Found a match " + mSavedPeerConfig); p2pConnectWithPinDisplay(mSavedPeerConfig); transitionTo(mGroupNegotiationState); } break; private void p2pConnectWithPinDisplay(WifiP2pConfig config) { WifiP2pDevice dev = fetchCurrentDeviceDetails(config); String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner()); try { Integer.parseInt(pin); notifyInvitationSent(pin, config.deviceAddress); } catch (NumberFormatException ignore) { // do nothing if p2pConnect did not return a pin } }
class GroupNegotiationState extends State { @Override public void enter() { if (DBG) logd(getName()); } @Override public boolean processMessage(Message message) { if (DBG) logd(getName() + message.toString()); switch (message.what) { case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT: case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT: if (DBG) logd(getName() + " go success"); break; case WifiMonitor.P2P_GROUP_STARTED_EVENT: mGroup = (WifiP2pGroup) message.obj; if (DBG) logd(getName() + " group started"); if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) { updatePersistentNetworks(NO_RELOAD); String devAddr = mGroup.getOwner().deviceAddress; mGroup.setNetworkId(mGroups.getNetworkId(devAddr, mGroup.getNetworkName())); } if (mGroup.isGroupOwner()) { if (!mAutonomousGroup) { mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S); } startDhcpServer(mGroup.getInterface()); } else { mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S); mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext, P2pStateMachine.this, mGroup.getInterface()); // TODO: We should use DHCP state machine PRE message like WifiStateMachine mWifiNative.setP2pPowerSave(mGroup.getInterface(), false); mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP); WifiP2pDevice groupOwner = mGroup.getOwner(); WifiP2pDevice peer = mPeers.get(groupOwner.deviceAddress); if (peer != null) { // update group owner details with peer details found at discovery groupOwner.updateSupplicantDetails(peer); mPeers.updateStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED); sendPeersChangedBroadcast(); } else { logw("Unknown group owner " + groupOwner); } } transitionTo(mGroupCreatedState); break;
class GroupCreatedState extends State { @Override public void enter() { if (DBG) logd(getName()); // Once connected, peer config details are invalid mSavedPeerConfig.invalidate(); mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); updateThisDevice(WifiP2pDevice.CONNECTED); //DHCP server has already been started if I am a group owner if (mGroup.isGroupOwner()) { setWifiP2pInfoOnGroupFormation(NetworkUtils.numericToInetAddress(SERVER_ADDRESS)); } // In case of a negotiation group, connection changed is sent // after a client joins. For autonomous, send now if (mAutonomousGroup) { sendP2pConnectionChangedBroadcast(); } }
case DhcpStateMachine.CMD_POST_DHCP_ACTION: DhcpResults dhcpResults = (DhcpResults) message.obj; if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS && dhcpResults != null) { if (DBG) logd("DhcpResults: " + dhcpResults); setWifiP2pInfoOnGroupFormation(dhcpResults.serverAddress); sendP2pConnectionChangedBroadcast(); //Turn on power save on client mWifiNative.setP2pPowerSave(mGroup.getInterface(), true); } else { loge("DHCP failed"); mWifiNative.p2pGroupRemove(mGroup.getInterface()); } break;
private void sendP2pConnectionChangedBroadcast() { if (DBG) logd("sending p2p connection changed broadcast"); Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo)); intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo)); intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, new WifiP2pGroup(mGroup)); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); mWifiChannel.sendMessage(WifiP2pService.P2P_CONNECTION_CHANGED, new NetworkInfo(mNetworkInfo)); }
case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: //We let the supplicant handle the provision discovery response //and wait instead for the GO_NEGOTIATION_REQUEST_EVENT. //Handling provision discovery and issuing a p2p_connect before //group negotiation comes through causes issues break;
case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT: config = (WifiP2pConfig) message.obj; if (isConfigInvalid(config)) { loge("Dropping GO neg request " + config); break; } mSavedPeerConfig = config; mAutonomousGroup = false; mJoinExistingGroup = false; transitionTo(mUserAuthorizingNegotiationRequestState); break;
class UserAuthorizingNegotiationRequestState extends State { @Override public void enter() { if (DBG) logd(getName()); notifyInvitationReceived(); }
case PEER_CONNECTION_USER_ACCEPT: mWifiNative.p2pStopFind(); p2pConnectWithPinDisplay(mSavedPeerConfig); mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED); sendPeersChangedBroadcast(); transitionTo(mGroupNegotiationState); break;
case WifiP2pManager.CONNECT: WifiP2pConfig config = (WifiP2pConfig) message.obj; if (isConfigInvalid(config)) { loge("Dropping connect requeset " + config); replyToMessage(message, WifiP2pManager.CONNECT_FAILED); break; } logd("Inviting device : " + config.deviceAddress); mSavedPeerConfig = config; if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) { mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED); sendPeersChangedBroadcast(); replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED); } else { replyToMessage(message, WifiP2pManager.CONNECT_FAILED, WifiP2pManager.ERROR); } // TODO: figure out updating the status to declined when invitation is rejected break;
case WifiMonitor.P2P_INVITATION_RESULT_EVENT: P2pStatus status = (P2pStatus)message.obj; if (status == P2pStatus.SUCCESS) { // invocation was succeeded. break; } loge("Invitation result " + status); if (status == P2pStatus.UNKNOWN_P2P_GROUP) { // target device has already removed the credential. // So, remove this credential accordingly. int netId = mGroup.getNetworkId(); if (netId >= 0) { if (DBG) logd("Remove unknown client from the list"); if (!removeClientFromList(netId, mSavedPeerConfig.deviceAddress, false)) { // not found the client on the list loge("Already removed the client, ignore"); break; } // try invitation. sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig); } } break;
case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj; mSavedPeerConfig = new WifiP2pConfig(); mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress; if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) { mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD; } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) { mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; mSavedPeerConfig.wps.pin = provDisc.pin; } else { mSavedPeerConfig.wps.setup = WpsInfo.PBC; } transitionTo(mUserAuthorizingJoinState); break;
class UserAuthorizingJoinState extends State { @Override public void enter() { if (DBG) logd(getName()); notifyInvitationReceived(); } @Override public boolean processMessage(Message message) { if (DBG) logd(getName() + message.toString()); switch (message.what) { case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: //Ignore more client requests break; case PEER_CONNECTION_USER_ACCEPT: //Stop discovery to avoid failure due to channel switch mWifiNative.p2pStopFind(); if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) { mWifiNative.startWpsPbc(mGroup.getInterface(), null); } else { mWifiNative.startWpsPinKeypad(mGroup.getInterface(), mSavedPeerConfig.wps.pin); } transitionTo(mGroupCreatedState); break; case PEER_CONNECTION_USER_REJECT: if (DBG) logd("User rejected incoming request"); transitionTo(mGroupCreatedState); break; default: return NOT_HANDLED; } return HANDLED; }
case WifiMonitor.AP_STA_CONNECTED_EVENT: WifiP2pDevice device = (WifiP2pDevice) message.obj; String deviceAddress = device.deviceAddress; // Clear timeout that was set when group was started. mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0); if (deviceAddress != null) { if (mPeers.get(deviceAddress) != null) { mGroup.addClient(mPeers.get(deviceAddress)); } else { mGroup.addClient(deviceAddress); } mPeers.updateStatus(deviceAddress, WifiP2pDevice.CONNECTED); if (DBG) logd(getName() + " ap sta connected"); sendPeersChangedBroadcast(); } else { loge("Connect on null device address, ignore"); } sendP2pConnectionChangedBroadcast(); break;
case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT: WifiP2pGroup group = (WifiP2pGroup) message.obj; WifiP2pDevice owner = group.getOwner(); if (owner == null) { loge("Ignored invitation from null owner"); break; } config = new WifiP2pConfig(); config.deviceAddress = group.getOwner().deviceAddress; if (isConfigInvalid(config)) { loge("Dropping invitation request " + config); break; } mSavedPeerConfig = config; if ((owner = mPeers.get(owner.deviceAddress)) != null) { if (owner.wpsPbcSupported()) { mSavedPeerConfig.wps.setup = WpsInfo.PBC; } else if (owner.wpsKeypadSupported()) { mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD; } else if (owner.wpsDisplaySupported()) { mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; } } mAutonomousGroup = false; mJoinExistingGroup = true; transitionTo(mUserAuthorizingInviteRequestState); break;
class UserAuthorizingInviteRequestState extends State { @Override public void enter() { if (DBG) logd(getName()); notifyInvitationReceived(); } @Override public boolean processMessage(Message message) { if (DBG) logd(getName() + message.toString()); boolean ret = HANDLED; switch (message.what) { case PEER_CONNECTION_USER_ACCEPT: mWifiNative.p2pStopFind(); if (!reinvokePersistentGroup(mSavedPeerConfig)) { // Do negotiation when persistence fails p2pConnectWithPinDisplay(mSavedPeerConfig); } mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED); sendPeersChangedBroadcast(); transitionTo(mGroupNegotiationState); break;