blob: 44a699547acd2cdffe2d7ea30c03ad0816dda3ad [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.bips.p2p;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.NetworkInfo;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pGroup;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Looper;
import android.util.Log;
import com.android.bips.BuiltInPrintService;
import com.android.bips.DelayedAction;
import com.android.bips.util.BroadcastMonitor;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Manage the process of connecting to a previously discovered P2P device
*/
public class P2pConnectionProcedure extends BroadcastReceiver {
private static final String TAG = P2pConnectionProcedure.class.getSimpleName();
private static final boolean DEBUG = false;
private static final int P2P_CONNECT_DELAYED_PERIOD = 3000;
private final BuiltInPrintService mService;
private final WifiP2pManager mP2pManager;
private final WifiP2pDevice mPeer;
private final BroadcastMonitor mConnectionMonitor;
private final List<P2pConnectionListener> mListeners = new CopyOnWriteArrayList<>();
private WifiP2pManager.Channel mChannel;
private String mNetwork;
private WifiP2pInfo mInfo;
private boolean mInvited = false;
private boolean mDelayed = false;
private DelayedAction mDetectDelayed;
P2pConnectionProcedure(BuiltInPrintService service, WifiP2pManager p2pManager,
WifiP2pDevice peer, P2pConnectionListener listener) {
mService = service;
mP2pManager = p2pManager;
mPeer = peer;
mConnectionMonitor = service.receiveBroadcasts(this,
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION,
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
if (DEBUG) Log.d(TAG, "Connecting to " + mPeer.deviceAddress);
mChannel = mP2pManager.initialize(service, Looper.getMainLooper(), null);
mListeners.add(listener);
mP2pManager.connect(mChannel, configForPeer(peer), null);
}
private WifiP2pConfig configForPeer(WifiP2pDevice peer) {
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = peer.deviceAddress;
if (peer.wpsPbcSupported()) {
config.wps.setup = WpsInfo.PBC;
} else if (peer.wpsKeypadSupported()) {
config.wps.setup = WpsInfo.KEYPAD;
} else {
config.wps.setup = WpsInfo.DISPLAY;
}
return config;
}
/** Return the peer associated with this connection procedure */
public WifiP2pDevice getPeer() {
return mPeer;
}
/** Return true if the specified listener is currently listening to this object */
boolean hasListener(P2pConnectionListener listener) {
return mListeners.contains(listener);
}
void addListener(P2pConnectionListener listener) {
if (mInfo != null) {
listener.onConnectionOpen(mNetwork, mInfo);
}
mListeners.add(listener);
}
void removeListener(P2pConnectionListener listener) {
mListeners.remove(listener);
}
int getListenerCount() {
return mListeners.size();
}
/** Close this connection */
public void close() {
if (DEBUG) Log.d(TAG, "stop() for " + mPeer.deviceAddress);
mListeners.clear();
mConnectionMonitor.close();
if (mDetectDelayed != null) {
mDetectDelayed.cancel();
}
if (mChannel != null) {
mP2pManager.cancelConnect(mChannel, null);
mP2pManager.removeGroup(mChannel, null);
mChannel.close();
mChannel = null;
}
}
@Override
public void onReceive(Context context, Intent intent) {
if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(intent.getAction())) {
NetworkInfo network = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
WifiP2pGroup group = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
WifiP2pInfo info = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
if (DEBUG) Log.d(TAG, "Connection state=" + network.getState());
if (network.isConnected()) {
if (isConnectedToPeer(group)) {
if (DEBUG) Log.d(TAG, "Group=" + group.getNetworkName() + ", info=" + info);
if (mDelayed) {
// We notified a delay in the past, remove this
for (P2pConnectionListener listener : mListeners) {
listener.onConnectionDelayed(false);
}
} else {
// Cancel any future delayed indications
if (mDetectDelayed != null) {
mDetectDelayed.cancel();
}
}
mNetwork = group.getInterface();
mInfo = info;
for (P2pConnectionListener listener : mListeners) {
listener.onConnectionOpen(mNetwork, mInfo);
}
}
} else if (mInvited) {
// Only signal connection closure if we reached the invitation phase
for (P2pConnectionListener listener : mListeners) {
listener.onConnectionClosed();
}
close();
}
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(intent.getAction())) {
WifiP2pDeviceList list = intent.getParcelableExtra(
WifiP2pManager.EXTRA_P2P_DEVICE_LIST);
WifiP2pDevice device = list.get(mPeer.deviceAddress);
if (DEBUG) Log.d(TAG, "Peers changed, device is " + P2pMonitor.toString(device));
if (!mInvited && device != null && device.status == WifiP2pDevice.INVITED) {
// Upon first invite, start timer to detect delayed connection
mInvited = true;
mDetectDelayed = mService.delay(P2P_CONNECT_DELAYED_PERIOD, () -> {
mDelayed = true;
for (P2pConnectionListener listener : mListeners) {
listener.onConnectionDelayed(true);
}
});
}
}
}
/** Return true if group is connected to the peer */
private boolean isConnectedToPeer(WifiP2pGroup group) {
WifiP2pDevice owner = group.getOwner();
if (owner != null && owner.deviceAddress.equals(mPeer.deviceAddress)) {
return true;
}
for (WifiP2pDevice client : group.getClientList()) {
if (client.deviceAddress.equals(mPeer.deviceAddress)) {
return true;
}
}
return false;
}
}