这一章主要总结从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;