2018/10/30

[Android] WIFI Direct Connection

一般做UDP傳輸前,裝置的wifi網路都要先連接到共同一個網域(AP)底下,才能透過IP來進行溝通。
但是如果可以直接利用wifi direct的方式連接的話,不需要連接AP就可以直接溝通了。
在MainActivity.java中,透過implement WifiP2pManager.PeerListListener, 以及WifiP2pManager.ConnectionInfoListener來做。
這個寫法是直接搜尋附近最近一個可以連線的裝置,找到之後會自動連線。

完整程式碼 MainActivity.java:
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
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.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity implements WifiP2pManager.PeerListListener, WifiP2pManager.ConnectionInfoListener {

    private final String TAG = "MainActivity";

    private boolean isDeviceFound = false;

    private TextView tvMacAddress, tvConnectDevice, tvConnectStatus;

    private WifiP2pManager.Channel mChannel;
    private WifiP2pManager mWifiP2pManager;
    private final IntentFilter intentFilter = new IntentFilter();
    private List peers = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // find view by id
        tvMacAddress = (TextView)findViewById(R.id.id_tvMacAddress);
        tvConnectDevice = (TextView)findViewById(R.id.id_tvConnectDevice);
        tvConnectStatus = (TextView)findViewById(R.id.id_tvConnectStatus);

        disconnectFunc();

        WifiManager mWifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        BluetoothAdapter myDevice = BluetoothAdapter.getDefaultAdapter();
        if (mWifiManager != null) {
            // Turn on wifi
            mWifiManager.setWifiEnabled(true);
            // get my device and mac address
            tvMacAddress.setText(myDevice.getName() + ", MAC[" + mWifiManager.getConnectionInfo().getMacAddress() + "]");
        }

        // setup wifi p2p
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
        registerReceiver(mReceiver, intentFilter);

        mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
        mChannel = mWifiP2pManager.initialize(this, getMainLooper(), null);
        mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                Log.d(TAG,"discoverPeers onSuccess");
            }

            @Override
            public void onFailure(int reasonCode) {
                Log.d(TAG,"discoverPeers onFailure");
            }
        });
    }

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
                Log.d(TAG,"WIFI_P2P_STATE_CHANGED_ACTION");
            }
            else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
                Log.d(TAG,"WIFI_P2P_PEERS_CHANGED_ACTION");
                if (mWifiP2pManager != null) {
                    mWifiP2pManager.requestPeers(mChannel, MainActivity.this);
                }
            }
            else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
                Log.d(TAG,"WIFI_P2P_CONNECTION_CHANGED_ACTION");
                if (mWifiP2pManager == null) {
                    return;
                }
                NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
                if (networkInfo.isConnected()) {
                    mWifiP2pManager.requestConnectionInfo(mChannel, MainActivity.this);
                }
            }
            else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
                Log.d(TAG,"WIFI_P2P_THIS_DEVICE_CHANGED_ACTION");
            }
        }
    };

    // search available devices and auto connect first one
    @Override
    public void onPeersAvailable(WifiP2pDeviceList peerList) {
        peers.clear();
        peers.addAll(peerList.getDeviceList());
        String[] peersMacAddress, peersDeviceName;
        if (peers.size() == 0) {
            Log.d(TAG, "No devices found");
            tvConnectStatus.setText("no connected");
            peersMacAddress = new String[1];
            peersMacAddress[0]="No Devices found";
            peersDeviceName = new String[1];
            peersDeviceName[0]="No Devices found";
        }
        else{
            peersMacAddress = new String[peers.size()];
            peersDeviceName = new String[peers.size()];
            int i=0, j=0;
            for(WifiP2pDevice device: peers){
                peersDeviceName[i++] = device.deviceName;
                peersMacAddress[j++] = device.deviceAddress;

                // find the first device and auto connect
                if(device.deviceAddress.equals(peersMacAddress[0])){
                    tvConnectDevice.setText(peersDeviceName[0] + ", MAC[" + peersMacAddress[0] + "]");
                    if(!isDeviceFound){
                        connectFunc();
                        isDeviceFound = true;
                    }
                }
            }
        }
    }

    // check connect status
    @Override
    public void onConnectionInfoAvailable(WifiP2pInfo info) {
        tvConnectDevice.setText("Stop Searching");
        tvConnectStatus.setText("Device already connected");
    }

    private void connectFunc(){
        Log.d(TAG, "connectFunc");
        WifiP2pDevice device = peers.get(0);
        WifiP2pConfig config = new WifiP2pConfig();
        config.deviceAddress = device.deviceAddress;
        config.wps.setup = WpsInfo.PBC;
        mWifiP2pManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                Log.d(TAG,"connect success");
                tvConnectStatus.setText("device connected");
            }

            @Override
            public void onFailure(int reason) {
                Log.d(TAG,"connect fail");
            }
        });
    }

    private void disconnectFunc(){
        Log.d(TAG, "disconnectFunc");
        if (mWifiP2pManager != null && mChannel != null) {
            mWifiP2pManager.requestGroupInfo(mChannel, new WifiP2pManager.GroupInfoListener() {
                @Override
                public void onGroupInfoAvailable(WifiP2pGroup group) {
                    if (group != null && mWifiP2pManager != null && mChannel != null && group.isGroupOwner()) {
                        mWifiP2pManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
                            @Override
                            public void onSuccess() {
                                Log.d(TAG, "removeGroup onSuccess -");
                            }

                            @Override
                            public void onFailure(int reason) {
                                Log.d(TAG, "removeGroup onFailure -" + reason);
                            }
                        });
                    }
                }
            });
        }
    }
}

補上xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.chris.myapplication.MainActivity">

    <TextView
        android:id="@+id/id_tvMyDevice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="My Device:"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/id_tvMacAddress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="My Mac Address"
        app:layout_constraintTop_toBottomOf="@+id/id_tvMyDevice" />

    <TextView
        android:id="@+id/id_tvDeviceFound"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Device Found:"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/id_tvMacAddress" />

    <TextView
        android:id="@+id/id_tvConnectDevice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Searching..."
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/id_tvDeviceFound" />

    <TextView
        android:id="@+id/id_tvConnectStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="no connected"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/id_tvConnectDevice" />

</android.support.constraint.ConstraintLayout>


在AndroidManifest.xml加入權限:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

連線成功之後,接下來有時間再來寫加入UDP傳輸試試看。
END

沒有留言:

張貼留言