Wifi-Direct 사용하기 (WifiP2pManager)

|

안드로이드 ICS(아이스크림샌드위치) 부터 Wifi-Direct를 API차원에서 지원해준다.


먼저, Wifi-Direct란 무엇인가.

일반적인 Wifi 연결은 클라이언트모드로 기존 AP에 붙거나, AP모드(핫스팟)로 자기 자신이 AP가 되거나 하는 Infrastructure모드로 연결이 가능했지만,

Wifi-Direct는 Ad-hoc모드인 1:1 (그룹핑으로 1:N도 가능) 연결을 AP 없이 가능하도록 한 Wifi 표준이다.


안드로이드에서 Wifi-Direct를 사용하기 위한 메인 클래스는 WifiP2pDirect라는 클래스인데,

이 클래스(와 관련 클래스)가 send/receive까지 처리해주는것은 아니고, 네트워크 망만 만들어준다.

연결하고, 데이터를 전송하기 위해선 IP기반의 연결 송수신 처리가 필요하다.


WifiP2pDirect 예제는 SDK Sample 내에 있으며 (물론 ICS이상의 Sample을 받아야 함)

이 소스를 분석하여 기본적인 사용 방법을 정리해 본다.


1. 서비스 obtain 및 초기화


WifiP2pService manager = (WifiP2pService)getSystemService(Context.WIFI_P2P_SERVICE);
Channel channel = manager.initialize(this, getMainLooper(), null);

시스템으로부터 WIFI_P2P_SERVICE를 받아와 init을 수행하는데, init의 첫번째 인자, Context는 항상 보는거니 넘어가고,

두번째는 콜백을 받을 Looper, 세번째는 프레임워크로부터 연결이 끊어졌을때의 리스너인데 null값이 가능하다.


channel이 정상적으로 받아와졌다면 이후 처리를 수행하면 된다.


WifiP2pManager를 전반적으로 사용하기 위해서는 ACCESS_WIFI_STATE와 CHANGE_WIFI_STATE 퍼미션이 필요하고,

IP기반 통신을 위해 포트를 열 것이라면 INTERNET 퍼미션 또한 필요하다.


Wifi-Direct가 현재 사용 가능한지는 브로드캐스트로 확인할 수 있는데,

WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION 브로드캐스트를 등록하면,

등록 시점에 Wifi-Direct가 사용가능한 경우 바로 리시버로 들어오게 된다. 플래그변수등으로 확인하면 될듯.


테스트해보기로는 ICS 4.1.2에서(갤럭시S3에서 확인), Wifi를 끄고 켜는 시점에 Wifi-Direct기능도 같이 동작하며,

단말 자체의 Wifi-Direct검색은 Wifi 설정화면쪽에 하위 화면으로 존재한다.


하지만 ICS 4.0.4 (옵티머스 LTE에서 확인)에서는 Wifi-Direct를 사용하려면 Wifi는 꺼져있어야 하고,

설정에서 추가설정에 있는 Wifi-Direct 항목으로 들어가, 사용에 체크를 해 주어야 한다.




2. Peer 검색


WifiP2pManager가 정상적으로 초기화되었다면 주변의 peer를 검색해야 한다. 검색 후 peer 목록을 받는 순서이다.


manager.discoveryPeers(channel, new ActionListener() {
    @Override
    public void onFailure(int reason) {
        Log.w("WIFI", "discoveryPeers() Failed : " + reason);
    }
    @Override
    public void onSuccess() {
        Log.d("WIFI", "discoveryPeers() success");
        //여기서 requestPeers()를 호출하지 않음
    }
});

.......
//WIFI_P2P_PEERS_CHANGED_ACTION 수신시
manager.requestPeers(channel, new PeerListListener() {
    @Override
    public void onPeersAvailable(WifiP2pDeviceList deviceList) {
        Collection<WifiP2pDevice> devList = deviceList.getDeviceList();
        //추가 처리....
    });



연결하고자 하는 단말 양쪽에서 discoveryPeer()를 해야 서로가 검색이 가능한것으로 보인다.

requestPeer()은 실제적으로 검색하는것이 아닌, 프레임워크 캐시(?)에 저장된 목록을 받아오는것으로 보인다.


discoveryPeers()에 리스너를 붙일 수 있고, 이 리스너는 onSuccess() 와 onFailed()를 구현할 수 있는데,

onSuccess()는 단순히 프레임워크로 해당 요청을 성공적으로 전달했다는 의미이다.

discoveryPeer()에서 onSuccess()가 호출되었다고 해서 피어 검색이 완료된건 아니라는 의미이다.

또한, 가끔 discoveryPeer()가 BUSY라든가, Internal Error가 발생하는 경우가 있는데, 이 경우 두세번 재시도를 하면 성공이 떨어지는 경우도 있으니 참고. (BUSY오류는 Wifi-Direct 기능이 꺼져있는경우에도 이 에러코드로 떨어진다)


그렇다면, 실제적인 피어 업데이트는 언제 수행하느냐,

바로 브로드캐스트를 사용해서 업데이트를 수행한다.


시스템이 갖고있는 피어 목록이 업데이트되면 WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION 브로드캐스트가 들어오는데,

이때 시스템으로 피어 목록을 요청하여 처리하면 된다.


요약하자면, 피어 검색을 위한 순서는

discoveryPeer() -> WIFI_P2P_PEERS_CHANGED_ACTION 브로드캐스트 수신 -> requestPeer() -> requestPeer()에 리스너로 등록한 onPeersAvailable()에서 실제 피어 리스트 확인 가능


의 순서로 처리하면 된다.




3. 연결 요청


목록을 받아왔으면, 선택 디바이스에 대해 연결을 요청할 수 있다.

연결 요청시에는 mac address가 필요하고, 이 값은 위 WifiP2pDevice 클래스 내에서 가져올 수 있다.


WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress; //device는 WifiP2pDevice의 인스턴스
config.wps.setup = WpsInfo.PBC;

manager.connect(channel, config, null);

.....

//WIFI_P2P_CONNECTION_CHANGED_ACTION수신시
NetworkInfo netInfo = (NetworkInfo)intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
Log.i("WIFI", "Wifi-Direct connected : " + netInfo.isConnected());


PBC는 Wifi 인증 방법중 하나라는데 자세한 내용은 추후 추가.

connect()에서 사용하는 리스너 역시 실제 연결 성립시 호출되는것이 아닌 시스템으로부터의 리턴이다. 쉽게 말해서 연결을 요청하는데 성공(실패)했다.. 라는 의미로 보면 된다.


한쪽에서 연결을 요청하게 되면 다른쪽에서는 팝업이 뜨며, 연결을 수락할것인지 뜨게 된다.

이 팝업은 시스템에서 띄워주는것으로, 어플에서의 별도 처리는 필요 없다.

(갤럭시S3는 30초후 자동수락 팝업으로 표시되며, 옵티머스LTE는  단순 yes/no 팝업으로 표시된다)

이 팝업에서 [예]를 누르게 되면 시스템에서 연결 처리를 수행하게 된다.


연결시에도 마찬가지로 브로드캐스트로 연결 상태 변경 메세지가 들어오게 되며, 이 Action은,

WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION 이다.


이 Action에는 WifiP2pManager.EXTRA_NETWORK_INFO 키값으로 ParcelableExtra가 넘어오고,

기본적인 연결 가/부 상태는 이 데이터로부터 받은 NetworkInfo.isConnected()로 알 수 있다.

이 브로드캐스트 역시 브로드캐스트 등록시에 현재 상태가 한번 날아온다.



4. 이후 처리

Wifi-Direct는 1:1연결이라도 Group Owner가 존재한다. Group Owner가 정해지는 기준은

연결을 요청한쪽이라든가 연결을 받은쪽 등 기준이 확실하지 않다. 문서상에도 group owner가 되는 기준이 명시되어 있지 않다.


group owner의 여부는 WifiP2pManager.requestConnectionInfo()의 리스너인 ConnectionInfoListener()의 콜백 메소드,

onConnectionInfoAvailable()의 인자로 WifiP2pInfo 형의 인스턴스로 제공된다.


이 인자에 isGroupOwner의 boolean값이 있으며, GroupOwner가 아닌 경우, groupOwnerAddress.getHostAddress()로 상대방 IP를 알 수 있다.




5. 추가 내용

1:N 연결이 가능하지만, 기본적으로 1:1연결을 목적으로 하는데,

Group Owner인 경우 상대방 IP를 알 수 있는 API가 존재하지 않는다.


GroupOwner의 경우에 상대방 IP가 필요한 경우는 다른 꼼수를 통해 받아와야 하는 듯 싶다.


위에서 나온 브로드캐스트 외에도 WIFI_P2P_THIS_DEVICE_CHANGED_ACTION 이라는 브로드캐스트도 있다.


Wifi-Direct는 주변 기기와의 호환을 위한 UPnP와 Bonjour 서비스와의 접목도 가능하도록 구성되어 있다.




And