Home | History | Annotate | Download | only in p2p
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.bips.p2p;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.net.NetworkInfo;
     23 import android.net.wifi.WpsInfo;
     24 import android.net.wifi.p2p.WifiP2pConfig;
     25 import android.net.wifi.p2p.WifiP2pDevice;
     26 import android.net.wifi.p2p.WifiP2pDeviceList;
     27 import android.net.wifi.p2p.WifiP2pGroup;
     28 import android.net.wifi.p2p.WifiP2pInfo;
     29 import android.net.wifi.p2p.WifiP2pManager;
     30 import android.os.Looper;
     31 import android.util.Log;
     32 
     33 import com.android.bips.BuiltInPrintService;
     34 import com.android.bips.DelayedAction;
     35 import com.android.bips.util.BroadcastMonitor;
     36 
     37 import java.util.List;
     38 import java.util.concurrent.CopyOnWriteArrayList;
     39 
     40 /**
     41  * Manage the process of connecting to a previously discovered P2P device
     42  */
     43 public class P2pConnectionProcedure extends BroadcastReceiver {
     44     private static final String TAG = P2pConnectionProcedure.class.getSimpleName();
     45     private static final boolean DEBUG = false;
     46 
     47     private static final int P2P_CONNECT_DELAYED_PERIOD = 3000;
     48 
     49     private final BuiltInPrintService mService;
     50     private final WifiP2pManager mP2pManager;
     51     private final WifiP2pDevice mPeer;
     52     private final BroadcastMonitor mConnectionMonitor;
     53     private final List<P2pConnectionListener> mListeners = new CopyOnWriteArrayList<>();
     54     private WifiP2pManager.Channel mChannel;
     55     private String mNetwork;
     56     private WifiP2pInfo mInfo;
     57     private boolean mInvited = false;
     58     private boolean mDelayed = false;
     59     private DelayedAction mDetectDelayed;
     60 
     61     P2pConnectionProcedure(BuiltInPrintService service, WifiP2pManager p2pManager,
     62             WifiP2pDevice peer, P2pConnectionListener listener) {
     63         mService = service;
     64         mP2pManager = p2pManager;
     65         mPeer = peer;
     66         mConnectionMonitor = service.receiveBroadcasts(this,
     67                 WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION,
     68                 WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
     69         if (DEBUG) Log.d(TAG, "Connecting to " + mPeer.deviceAddress);
     70         mChannel = mP2pManager.initialize(service, Looper.getMainLooper(), null);
     71         mListeners.add(listener);
     72         mP2pManager.connect(mChannel, configForPeer(peer), null);
     73     }
     74 
     75     private WifiP2pConfig configForPeer(WifiP2pDevice peer) {
     76         WifiP2pConfig config = new WifiP2pConfig();
     77         config.deviceAddress = peer.deviceAddress;
     78         if (peer.wpsPbcSupported()) {
     79             config.wps.setup = WpsInfo.PBC;
     80         } else if (peer.wpsKeypadSupported()) {
     81             config.wps.setup = WpsInfo.KEYPAD;
     82         } else {
     83             config.wps.setup = WpsInfo.DISPLAY;
     84         }
     85         return config;
     86     }
     87 
     88     /** Return the peer associated with this connection procedure */
     89     public WifiP2pDevice getPeer() {
     90         return mPeer;
     91     }
     92 
     93     /** Return true if the specified listener is currently listening to this object */
     94     boolean hasListener(P2pConnectionListener listener) {
     95         return mListeners.contains(listener);
     96     }
     97 
     98     void addListener(P2pConnectionListener listener) {
     99         if (mInfo != null) {
    100             listener.onConnectionOpen(mNetwork, mInfo);
    101         }
    102         mListeners.add(listener);
    103     }
    104 
    105     void removeListener(P2pConnectionListener listener) {
    106         mListeners.remove(listener);
    107     }
    108 
    109     int getListenerCount() {
    110         return mListeners.size();
    111     }
    112 
    113     /** Close this connection */
    114     public void close() {
    115         if (DEBUG) Log.d(TAG, "stop() for " + mPeer.deviceAddress);
    116         mListeners.clear();
    117         mConnectionMonitor.close();
    118         if (mDetectDelayed != null) {
    119             mDetectDelayed.cancel();
    120         }
    121         if (mChannel != null) {
    122             mP2pManager.cancelConnect(mChannel, null);
    123             mP2pManager.removeGroup(mChannel, null);
    124             mChannel.close();
    125             mChannel = null;
    126         }
    127     }
    128 
    129     @Override
    130     public void onReceive(Context context, Intent intent) {
    131         if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(intent.getAction())) {
    132             NetworkInfo network = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
    133             WifiP2pGroup group = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
    134             WifiP2pInfo info = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
    135 
    136             if (DEBUG) Log.d(TAG, "Connection state=" + network.getState());
    137 
    138             if (network.isConnected()) {
    139                 if (isConnectedToPeer(group)) {
    140                     if (DEBUG) Log.d(TAG, "Group=" + group.getNetworkName() + ", info=" + info);
    141                     if (mDelayed) {
    142                         // We notified a delay in the past, remove this
    143                         for (P2pConnectionListener listener : mListeners) {
    144                             listener.onConnectionDelayed(false);
    145                         }
    146                     } else {
    147                         // Cancel any future delayed indications
    148                         if (mDetectDelayed != null) {
    149                             mDetectDelayed.cancel();
    150                         }
    151                     }
    152 
    153                     mNetwork = group.getInterface();
    154                     mInfo = info;
    155                     for (P2pConnectionListener listener : mListeners) {
    156                         listener.onConnectionOpen(mNetwork, mInfo);
    157                     }
    158                 }
    159             } else if (mInvited) {
    160                 // Only signal connection closure if we reached the invitation phase
    161                 for (P2pConnectionListener listener : mListeners) {
    162                     listener.onConnectionClosed();
    163                 }
    164                 close();
    165             }
    166         } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(intent.getAction())) {
    167             WifiP2pDeviceList list = intent.getParcelableExtra(
    168                     WifiP2pManager.EXTRA_P2P_DEVICE_LIST);
    169             WifiP2pDevice device = list.get(mPeer.deviceAddress);
    170             if (DEBUG) Log.d(TAG, "Peers changed, device is " + P2pMonitor.toString(device));
    171 
    172             if (!mInvited && device != null && device.status == WifiP2pDevice.INVITED) {
    173                 // Upon first invite, start timer to detect delayed connection
    174                 mInvited = true;
    175                 mDetectDelayed = mService.delay(P2P_CONNECT_DELAYED_PERIOD, () -> {
    176                     mDelayed = true;
    177                     for (P2pConnectionListener listener : mListeners) {
    178                         listener.onConnectionDelayed(true);
    179                     }
    180                 });
    181             }
    182         }
    183     }
    184 
    185     /** Return true if group is connected to the peer */
    186     private boolean isConnectedToPeer(WifiP2pGroup group) {
    187         WifiP2pDevice owner = group.getOwner();
    188         if (owner != null && owner.deviceAddress.equals(mPeer.deviceAddress)) {
    189             return true;
    190         }
    191         for (WifiP2pDevice client : group.getClientList()) {
    192             if (client.deviceAddress.equals(mPeer.deviceAddress)) {
    193                 return true;
    194             }
    195         }
    196         return false;
    197     }
    198 }
    199