JUnitについての書籍が遂に発売します!
Java開発時のユニットテストを加速する「JUnit速効レシピ」
Java開発時のユニットテストを加速する「JUnit速効レシピ」
Androidでは、アプリによってWifiの接続先を切り替える事が出来ます。
今回は、そんな接続先の切り替えを、各無線LANスポットの電波強度によって
自動的に判別し、一番電波状態の良い無線LANスポットへ自動的に接続を切り替える仕組みの
実装方法について解説します。
接続情報を取得する
Androidアプリ開発では、現在接続中のWifi情報を取得する事が出来ます。
具体的には、WifiManagerを使用します。
早速、WifiManagerのインスタンスを取得しましょう。
WifiManagerのインスタンスの取得は『getSystemService()』メソッドを介して行います。
WifiManager wifiManager = (WifiManager)getSystemService(WIFI_SERVICE);
インスタンスを取得しましたら、『getConnectionInfo()』メソッドを起動する事により、
現在接続中のWifi接続情報を保持する『WifiInfo』のオブジェクトを取得する事が出来ます。
WifiInfo info = wifiManager.getConnectionInfo();
接続中のWifi接続情報は全て、この『WifiInfo』から取得する事が出来ます。
『WifiInfo』から取得出来る情報と、取得する為の専用メソッドは下記の通りです。
| getBSSID() | BSSIDを取得します。 |
|---|---|
| getDetailedStateOf(SupplicantState suppState) | アプリカントの状態をマッピングします。 |
| getHiddenSSID() | ステルスモードの状態を取得します。 |
| getIpAddress() | IPアドレスを取得します。 |
| getLinkSpeed() | リンクスピードを取得します。 |
| getMacAddress() | MACアドレスを取得します。 |
| getNetworkId() | ネットワークIDを取得します。 |
| getRssi() | 電波強度を取得します。 |
| getSSID() | SSIDを取得します。 |
| getSupplicantState() | サプリカントの状態を取得します。 |
| toString() | これらの情報を文字列へシリアライズします。 |
周辺の無線LANスポットを検索する
現在検知しているWifiの一覧を取得するには、
『WifiManager』の『startScan()』メソッドを使用します。
// wifiマネージャーを取得する WifiManager manager = (WifiManager)getSystemService(WIFI_SERVICE); // 現在検知しているwifiスポットの検索を開始する manager.startScan();
『startScan()』メソッドによる検索結果は、『getScanResults()』メソッドにて取得する事が出来、
個々の情報は『ScanResult』オブジェクトとしてリストに纏められています。
それぞれに参照する場合は、『for()』等で回して扱いましょう。
『ScanResult』オブジェクトには、プロパティとして下記情報が格納されています。
| BSSID | MACアドレス |
|---|---|
| SSID | ネットワーク識別名 |
| capabilities | 暗号化情報 |
| frequency | 周波数 |
| level | 信号レベル |
| timestamp | 情報取得日時タイムスタンプ |
for(ScanResult result : manager.getScanResults()) {
Log.v("ScanResult.BSSID",result.BSSID);
Log.v("ScanResult.SSID",result.SSID);
Log.v("ScanResult.capabilities",result.capabilities);
Log.v("ScanResult.frequency",result.frequency);
Log.v("ScanResult.level",result.level);
Log.v("ScanResult.timestamp",result.timestamp);
}
接続履歴を取得する
無線LANスポットへ接続した経歴を取得する事が出来ます。
経歴といっても、「いつ・どこで」の様なヒストリーではなく、
接続実績のある無線LANスポットの一覧を取得出来ます。
その履歴には、過去に入力したパスワード等も含まれるため、再接続する際に必要となる情報です。
それでは早速履歴を取得して見ましょう。
履歴の取得は『WifiManager』のインスタンスから『getConfiguredNetworks()』メソッドにて
一覧を取得します。
具体的なコードは下記のとおりです。
// wifiマネージャーを取得する WifiManager manager = (WifiManager)getSystemService(WIFI_SERVICE); // 接続実績のあるwifi一覧を取得 Listconfig_list = manager.getConfiguredNetworks();
取得出来るのは、無線LAN個々の設定情報を保持した『WifiConfiguration』のリストです。
『WifiConfiguration』では、下記の情報をプロパティから取得する事が出来ます。
| BSSID | MACアドレス |
|---|---|
| SSID | ネットワーク識別名 |
| allowedAuthAlgorithms | 認証プロトコルセット |
| allowedGroupCiphers | グループの暗号セット |
| allowedKeyManagement | キー管理プロトコルのセット |
| allowedPairwiseCiphers | WPAのための暗号とのペア情報 |
| allowedProtocols | セキュリティプロトコルのセット |
| enterpriseConfig | EAP関連の詳細情報 |
| hiddenSSID | ステルスモードの状態 |
| networkId | ネットワークID |
| preSharedKey | WPA-PSKのキー |
| prioritye | 優先度 |
| status | ネットワークの状態 |
| wepKeys | WEPキー |
| wepTxKeyIndex | WEPキーのインデックス |
// wifiマネージャーを取得する WifiManager manager = (WifiManager)getSystemService(WIFI_SERVICE); // 接続実績のあるwifi一覧を取得 Listconfig_list = manager.getConfiguredNetworks(); // 接続経験のあるスポットを順に確認する for(int i=0; i < config_list.size(); i++) { Log.v("WifiConfiguration.BSSID",config_list.get(i).BSSID); Log.v("WifiConfiguration.SSID",config_list.get(i).SSID); Log.v("WifiConfiguration.allowedAuthAlgorithms",config_list.get(i).allowedAuthAlgorithms); Log.v("WifiConfiguration.allowedGroupCiphers",config_list.get(i).allowedGroupCiphers); Log.v("WifiConfiguration.allowedKeyManagement",config_list.get(i).allowedKeyManagement); Log.v("WifiConfiguration.allowedPairwiseCiphers",config_list.get(i).allowedPairwiseCiphers); Log.v("WifiConfiguration.allowedProtocols",config_list.get(i).allowedProtocols); Log.v("WifiConfiguration.enterpriseConfig",config_list.get(i).enterpriseConfig); Log.v("WifiConfiguration.hiddenSSID",config_list.get(i).hiddenSSID); Log.v("WifiConfiguration.networkId",config_list.get(i).networkId); Log.v("WifiConfiguration.preSharedKey",config_list.get(i).preSharedKey); Log.v("WifiConfiguration.prioritye",config_list.get(i).prioritye); Log.v("WifiConfiguration.status",config_list.get(i).status); Log.v("WifiConfiguration.wepKeys",config_list.get(i).wepKeys); Log.v("WifiConfiguration.wepTxKeyIndex",config_list.get(i).wepTxKeyIndex); }
電波強度の高い無線LANスポットへ自動的に接続を切り替える
それでは、本記事の目的でもある、電波強度の高い無線LANスポットへ、Wifi接続を自動的に切り替える
実装を行いたいと思います。
これまで解説して来た内容を組み合わせて、現在検知しているWifiと、過去に接続した事のある履歴と比較し、
合致するものの中から一番電波強度の高い無線LANスポットへ接続を切り替えます。
早速見て行きましょう。
まずはWifiを扱う時に必要となるマネージャーを取得し、接続履歴の取得、周囲のwifiのスキャンを行いましょう。
// wifiマネージャーを取得する WifiManager manager = (WifiManager)getSystemService(WIFI_SERVICE); // 接続履歴のあるwifi一覧を取得 Listconfig_list = manager.getConfiguredNetworks(); // 現在検知しているwifiスポットの検索を開始する manager.startScan();
続いて、取得した周囲のWifiと、接続履歴を比較します。
そして、合致するものに関しては『WifiManager』のスタティックメソッド『compareSignalLevel()』にて
電波の強度を比較し、より強いWifiの情報を退避させて起きます。
最終的に残ったWifi情報が、電波が一番強い接続実績のある無線LANスポットとなります。
また、注意する点としては、SSIDの情報にダブルクォーテーションが付いている場合があります。
もし付いていると文字列比較がし難くなってしまいますので、
今回のケースではダブルクォーテーションを除去しています。
// 一番強いWifi情報の受け皿を用意する
ScanResult best_signal = null;
// 検知しているwifiスポットを順に確認する
for(ScanResult result : manager.getScanResults()) {
// 接続経験のあるスポットを順に確認する
for(int i=0; i < config_list.size(); i++) {
// Android4.2以降よりダブルクォーテーションが付いてくるので除去
String ssid = config_list.get(i).SSID.replace("\"", "");
// 接続経験のあるスポットを検知していれば、情報を退避する
if(result.SSID.equals( ssid )){
// 初めと、後は比較して大きければ更新する
if (best_signal == null || WifiManager.compareSignalLevel(best_signal.level, result.level) < 0 ){
best_signal = result;
}
}
}
}
// 見つからなかったら終わり
if( best_signal == null ){
return false;
}
続いて、現在の接続情報を確認して、新しく接続しようとしているWifiスポットと同じであれば
何もせず、異なる場合は現在の接続を切断する処理を行いましょう。
// 現在の接続情報を取得
WifiInfo info = manager.getConnectionInfo();
// 現在と同じか確認する
if( info.getSSID().equals( best_signal.SSID.replace("\"", "") ) ){
// 既に接続済みなので、成功を返却する
return true;
}
else{
// 現在の接続を無効にする
manager.disableNetwork(info.getNetworkId());
// 接続の関連付けを行う
manager.enableNetwork(info.getNetworkId(), false);
}
現在の接続と同じか否かはSSIDの比較にて行います。
この際にも、ダブルクォーテーションを意識して除去を行っています。
重複する場合は即座にreturnし、異なる場合は『disableNetwork()』にて接続を強制的に無効にします。
無効にする事で接続を切断する事が出来ますが、このままでは無効のままとなり、再度接続しようとしても
認識する事が出来ません。
ですので、接続情報を無効から有効へ復帰させる必要があります。そこで、他との接続に影響を及ばさない様に、
『enableNetwork()』メソッドによる接続時に、第二引数へfalseを指定しています。
こうする事で、Wifiへの接続が向こうの状態から有効へ復帰します。
最後に、新しく取得した電波強度の一番高いWifiスポットへ接続します。
// 接続設定情報を構築する
WifiConfiguration config = new WifiConfiguration();
// Android4.2以降よりダブルクォーテーションが付いてくるので除去
config.SSID = best_signal.SSID.replace("\"", "");
// 接続に関する各設定を行う
config.hiddenSSID = true;
config.status = WifiConfiguration.Status.ENABLED;
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
config.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
// 接続する
if( manager.addNetwork(config) == -1 ){
// 接続に失敗
return false;
};
// 最新のコネクションへ強制的に接続する
manager.saveConfiguration();
manager.updateNetwork(config);
return manager.enableNetwork(config.networkId, true);
今度は、特定のWifiスポットへ接続しにいきますので、『enableNetwork()』メソッドの第二引数には『true』を指定し、
他の接続を破棄して強制的に目的のWifiスポットへ接続します。
これまでの処理を纏めると、下記の様になるかと思います。
public boolean bestWifiConnect() {
// wifiマネージャーを取得する
WifiManager manager = (WifiManager)getSystemService(WIFI_SERVICE);
// 接続履歴のあるwifi一覧を取得
List config_list = manager.getConfiguredNetworks();
// 現在検知しているwifiスポットの検索を開始する
manager.startScan();
// 一番強いWifi情報の受け皿を用意する
ScanResult best_signal = null;
// 検知しているwifiスポットを順に確認する
for(ScanResult result : manager.getScanResults()) {
// 接続経験のあるスポットを順に確認する
for(int i=0; i < config_list.size(); i++) {
// Android4.2以降よりダブルクォーテーションが付いてくるので除去
String ssid = config_list.get(i).SSID.replace("\"", "");
// 接続経験のあるスポットを検知していれば、情報を退避する
if(result.SSID.equals( ssid )){
// 初めと、後は比較して大きければ更新する
if (best_signal == null || WifiManager.compareSignalLevel(best_signal.level, result.level) < 0 ){
best_signal = result;
}
}
}
}
// 見つからなかったら終わり
if( best_signal == null ){
return false;
}
// 現在の接続情報を取得
WifiInfo info = manager.getConnectionInfo();
// 現在と同じか確認する
if( info.getSSID().equals( best_signal.SSID.replace("\"", "") ) ){
// 既に接続済みなので、成功を返却する
return true;
}
else{
// 現在の接続を無効にする
manager.disableNetwork(info.getNetworkId());
// 接続の関連付けを行う
manager.enableNetwork(info.getNetworkId(), false);
}
// 接続設定情報を構築する
WifiConfiguration config = new WifiConfiguration();
// Android4.2以降よりダブルクォーテーションが付いてくるので除去
config.SSID = best_signal.SSID.replace("\"", "");
// 接続に関する各設定を行う
config.hiddenSSID = true;
config.status = WifiConfiguration.Status.ENABLED;
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
config.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
// 接続する
if( manager.addNetwork(config) == -1 ){
// 接続に失敗
return false;
};
// 最新のコネクションへ強制的に接続する
manager.saveConfiguration();
manager.updateNetwork(config);
return manager.enableNetwork(config.networkId, true);
}
後は、作成した上記メソッドを定期的に実行し、電波状況を監視して常に最適なWifi環境を提供しましょう。
メソッドの定期実行には『Timer』を使用しましょう。そして、間に『Handler』を挟む事によって、
電波状況を切り替えた際のViewへのアクセスも可能としています。
下記サンプルでは、1秒毎の定期実行を行っています。
タイマーに関しては、後の停止処理の事を考えメンバ変数として扱っています。
Timer timer = null;
// 電波強度の高いwifiに接続する
public void bestWifiConnectStart() {
final Handler mHandler = new Handler();
this.timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
mHandler.post( new Runnable() {
public void run() {
// 接続を試みる
if( MainActivity.this.bestWifiConnect() ){
Log.v("connect","OK");
}
else{
Log.v("connect","NG");
}
}
});
}
},0,1000);
}
// タイマーを停止する
public void bestWifiConnectEnd() {
this.timer.cancel();
}
これで自動的に電波強度の高いWifiへの接続切り替えが可能となります。
こういった機能はiPhone/iPad/iPodアプリでは出来ないAndroid独自の特権の様な実装ですので、
使い所がありましたら、是非ともユーザエクスペリエンスの一環として、導入を検討して下さい。
JUnitについての書籍が遂に発売します!
Java開発時のユニットテストを加速する「JUnit速効レシピ」
Java開発時のユニットテストを加速する「JUnit速効レシピ」


Categories: