Android‎ > ‎

Android4.0 WiFiダイレクトについて少し調べてみた

ICSでandroid.net.wifi.p2p パッケージが増えたので、少し調べてみました。

クラス

  • WifiP2pManager
    • WiFiダイレクトのメインの処理を行うクラス
    • getSystemService() にWIFI_P2P_SERVICEを渡すとインスタンスがもらえる
  • WifiP2pManager.Channel
    • アプリが使用するチャンネル
    • WiFiP2pManagerのほとんどの処理に必要
    • 中身はprivateなので外からは何も見えない
  • WifiP2pDevice
    • 自分や検索した端末の情報
  • WifiP2pDeviceList
    • WiFiP2pDeviceのリストを持つクラス
    • ParcelでIntent受け渡しできるようにしてある
    • WiFiダイレクト検索で受け取れる
  • WifiP2pConfig
    • connect時のオプション
  • WifiP2pInfo
    • WiFiダイレクトの接続情報

必要なパーミッション

  • android.permission.ACCESS_WIFI_STATE
  • android.permission.CHANGE_WIFI_STATE

レシーバー

WiFiダイレクトでは、いくつかの情報が暗黙的Intentとして飛んできます。
そのため、BroadcastReceiverを使って適宜アクションを受信する必要があります。
受信するactionはWifiP2pManagerに定義されています。
  • WIFI_P2P_PEERS_CHANGED_ACTION
    • android.net.wifi.p2p.PEERS_CHANGE
    • 付近のPeer検索に変化があったことの通知 (いなくなったことは通知されない模様?)
  • WIFI_P2P_STATE_CHANGED_ACTION
    • android.net.wifi.p2p.STATE_CHANGED
    • 端末設定のWiFiダイレクトの設定が変更されたことの通知 (WiFiダイレクト自体の使用有無の変更)
    • EXTRA_WIFI_STATE で、stateが確認できる
  • WIFI_P2P_CONNECTION_CHANGED_ACTION
    • android.net.wifi.p2p.CONNECTION_STATE_CHANGE
    • WiFiダイレクトの接続状態が変化したことの通知
    • EXTRA_WIFI_P2P_INFO で、グループの情報が確認できる
    • EXTRA_NETWORK_INFO で、通信の情報が確認できる
  • WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
    • android.net.wifi.p2p.THIS_DEVICE_CHANGED
    • 自分の端末の状態が変化したことの通知 (接続したとか主にStatusが替わった)
    • EXTRA_WIFI_P2P_DEVICE で、端末のWiFiダイレクト情報が取得できる

その他

WiFiダイレクトは全てのAndroid端末が搭載しているわけではないので、
AndroidManifest.xmlには<uses-feature> として以下を記述するといいよとのこと。

<manifest ...>
   
<uses-feature android:name="android.hardware.wifi.direct" />
    ...
</manifest>

また、WiFiダイレクトは通常のWiFiと排他なので、端末がWiFiのAPに接続してしまうと、自動でOFFられてしまいます。
アプリ起動中はWiFiに繋がないようですが、一方がアプリ起動してなかったりするとOFFられてたりするので注意が必要です。


とりあえず使ってみる。

初期化

ActivityのonCreat()あたりで、以下のように呼び出します。

mP2PManager = (WifiP2pManager) getSystemService(WIFI_P2P_SERVICE);
mChannel = mP2PManager.initialize(this, getMainLooper(), null);
WiFiダイレクトを利用するアプリでは、初めに初期化を呼び出す必要があります。

public WifiP2pManager.Channel initialize (Context srcContext, Looper srcLooper, WifiP2pManager.ChannelListener listener)

Looperにはコールバックの返るスレッドを指定できます。基本UIスレッドへ返ってほしいと思うので、getMainLooper()でOKです。
渡したLooperが、Channelの持っているHandlerにバインドされるというだけです。

ChannelListenerは接続情報(切断時)のリスナーが用意されています。コールバックは、再初期化したい場合に利用できるようです。
特に必要なければnullで構いません。
Channelはアプリ終了までいろんなとこで使用するので、保持しておかないとイカンです。

レシーバーを動かす

常駐したいならAndroidManifest.xmlにいつも通り定義してください。
ApplicationやActivityに結び付けたいのであれば、以下のようにして起動させます。

IntentFilter filter = new IntentFilter();
filter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
filter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
filter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);

mReceiver = new HogeReceiver();
registerReceiver(mReceiver, filter);
止めるときは

unregisterReceiver(mReceiver);

付近の端末を検索する

近くにある端末の検索を開始します。

public void discoverPeers (WifiP2pManager.Channel c, WifiP2pManager.ActionListener listener)

discoverPeers() で発見される端末は、WiFiダイレクトがONになっている端末です。
このメソッドは結果を返さず、渡したチャンネルについて検索を開始するだけです。
ActionListenerには、onSuccess() と onFailure() の2つが定義されていて、実行の成否が返ります。(後者の場合はエラーコードのようであまり役立たない何かが返ってきます。)

検索結果はメソッドが返さないので、BroadcastReceiverで受信する必要があります。
WIFI_P2P_PEERS_CHANGED_ACTION を受信して、以下のように処理できます。

public class HogeReceiver extends BroadcastReceiver {
    ...     @Override     public void onReceive(Context context, Intent intent) {         String action = intent.getAction(); if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {             mP2PManager.requestPeers(myChannel, new PeerListListener() { @Override public void onPeersAvailable(WifiP2pDeviceList peers) {                     // 引数peersが検索された端末のリストにあたる。                     List<WifiP2pDevice> deviceList = peers.getDeviceList();                  ...                 }             });         }         ...     }
}
なかなか遠回りになりますが、こんな感じです。
実際はレシーバーだけで処理しないで、Activityなどにコールバックを実装して、そっちでrequestPeers() 以降の処理をしてあげるのがいいと思います。

WifiP2pDeviceListにはgetDeviceList() メソッドのみが定義されていて、WifiP2pDeviceのコレクションが取得できるようになっています。

端末に接続する

端末に接続する方法です。
WifiP2pDeviceインスタンスを持っていなくても、WiFiダイレクトがONになっている端末のMacアドレスがわかっていれば接続できるようです。(距離次第ですが

WifiP2pDevice device = ((List) peers.getDeviceList()).get(0);    // サンプルなので、検索結果の先頭の端末に接続
WifiP2pConfig config = new WifiP2pConfig(); config.deviceAddress = device.deviceAddress; config.wps.setup = WpsInfo.PBC; mP2PManager.connect(myChannel, config, new ActionListener() {     @Override     public void onSuccess() {         // 成功時     }     @Override     public void onFailure(int reason) { // 失敗時     } });
WifiP2pDeviceListから直接取得しちゃってますが、ユーザーに選択させる場合はListViewなりを活用してください。

例によって、ActionListenerはnullでも問題ありません。
成否のコールバックがありますが、この時点では接続できていないことに注意してください。
実際に接続が完了したことは、BroadcastReceiverのWIFI_P2P_CONNECTION_CHANGED_ACTION で受信できます。
接続が確立されるまでの間、端末同士はINVITEという状態になります。

public void connect (WifiP2pManager.Channel c, WifiP2pConfig config, WifiP2pManager.ActionListener listener)

接続時に重要なのが、2番目のWifiP2pConfigです。
この引数は、どの端末へ、どういった接続方法を取るかを指定できます。
といっても、Macアドレスさえ指定してやれば接続できます。

WifiP2pConfigの変数wps.setup では、接続時に相手にどういった通知がされるかを選択できます。
  • PBC
    • 相手端末にダイアログが表示され、ダイアログにあるボタンを押せば接続が開始される
    • 指定しなかった場合PBCになる?(GN - GNではPBCになりました)
  • PIN-DISPLAY
    • 自分の端末にランダムで生成されるPINコードが表示され、相手端末にPINコード入力用のダイアログが表示される
相手端末がどのWPSに対応しているかは、WifiP2pDeviceのパラメータとしてあらかじめ確認できます。
なぜか、WIFI_P2P_THIS_DEVICE_CHANGED_ACTION で取得できる自分の端末情報には記載されません。(falseになる)
相手から見るとtrueになるので、そういう仕様っぽいです。

WiFiダイレクトで接続すると、グループというものが生成されますが、このグループのオーナーは自動的に決まるようです。
createGroup() でグループ作れんのかな?と思ったけど、そこに参加できませんでした。ようわかりません(´・ω:;.:...

切断する

端末間の接続をやめる場合、端末の状態によって呼び出すメソッドが変わります。
状態というのは、WifiP2pDeviceのstatusのことです。

CONNECTED

public void removeGroup (WifiP2pManager.Channel c, WifiP2pManager.ActionListener listener)


INVITED

public void cancelConnect (WifiP2pManager.Channel c, WifiP2pManager.ActionListener listener)


引数は同じです。
端末間の接続が確立された場合は、removeGroup() を呼び出すと切断処理が開始されます。
即座に切断されるわけではなく、WIFI_P2P_CONNECTION_CHANGED_ACTION の通知が飛んできます。

INVITEDの場合はまだ接続参加処理中なので、中止してくれるようにします。
たまに、要請元がINVITEDになるけど、要請先が反応なくてハマることがあったりするのがよくわからんです(´・ω・`)
その場合は落ち着いてWiFiダイレクトをOFFってやればいいと思います。


ここまでが接続~切断までの流れです。
次があれば接続してからの処理について書くと思います。



Comments