| /* | |
| * 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 && !network.isConnectedOrConnecting()) { | |
| // 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; | |
| } | |
| } |