デバイスに完全なインターネットアクセスがあるかどうか、つまり、ユーザーがキャプティブポータル(walled gardenとも呼ばれる)、つまり制限されたサブネットに限定されていないことを確実に検出する必要があります完全なアクセス権を取得するために、ユーザーがフォームで資格情報を送信することを強制します。
私のアプリは認証プロセスを自動化しているため、ログオンアクティビティを開始する前に完全なインターネットアクセスが利用できないことを知っておくことが重要です。
問題はnotネットワークインターフェースがアップ状態で接続状態であることを確認する方法です。これは、サンドボックス化されたイントラネットセグメントとは対照的に、デバイスに無制限のインターネットアクセスがあることを確認することです。
既知のホストに接続しても例外はスローされず、すべてのリクエストがログインページにルーティングされるため、有効なHTTP 200
応答コードが返されるため、これまでに試したすべてのアプローチは失敗します。
ここに私が試したすべてのアプローチがありますが、上記で説明した理由により、true
ではなくfalse
を返します。
1:
InetAddress.getByName(Host).isReachable(TIMEOUT_IN_MILLISECONDS);
isConnected = true; <exception not thrown>
2:
Socket socket = new Socket();
SocketAddress sockaddr = new InetSocketAddress(InetAddress.getByName(Host), 80);
socket.connect(sockaddr, pingTimeout);
isConnected = socket.isConnected();
3:
URL url = new URL(hostUrl));
URLConnection urlConn = url.openConnection();
HttpURLConnection httpConn = (HttpURLConnection) urlConn;
httpConn.setAllowUserInteraction(false);
httpConn.setRequestMethod("GET");
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;
それでは、ログインリダイレクトページの代わりに実際のホストに接続していることをどうやって確認するのですか?明らかに、使用している「ping」ホストからの実際の応答本文を確認できましたが、適切なソリューションのようには見えません。
参考のために、Android 4.0.1 AOSPコードベースからの「公式」メソッドを次に示します。 WifiWatchdogStateMachine.isWalledGardenConnection() 。将来リンクが壊れる場合。
private static final String mWalledGardenUrl = "http://clients3.google.com/generate_204";
private static final int WALLED_GARDEN_SOCKET_TIMEOUT_MS = 10000;
private boolean isWalledGardenConnection() {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(mWalledGardenUrl); // "http://clients3.google.com/generate_204"
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
// We got a valid response, but not from the real google
return urlConnection.getResponseCode() != 204;
} catch (IOException e) {
if (DBG) {
log("Walled garden check - probably not a portal: exception "
+ e);
}
return false;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
このアプローチは特定のURLに依存します。mWalledGardenUrl = "http://clients3.google.com/generate_204"
は常に204
応答コードを返します。これは、DNSが干渉された場合でも機能します。その場合、予想される200
の代わりに204
コードが返されるためです。 インターネットにアクセスできないメッセージをAndroidデバイス。
Googleには、このテーマのバリエーションがあります。http://www.google.com/blank.html
を取得すると、長さがゼロの応答本文を持つ200
コードが返されます。したがって、空でない体を取得した場合、これは壁のある庭の後ろにいることを理解する別の方法になります。
Appleには、キャプティブポータルを検出するための独自のURLがあります。ネットワークが稼働している場合IOSで、MacOSデバイスは http://www.Apple.com/library/testのようなURLに接続します/success.html 、 http://attwifi.Apple.com/library/test/success.html 、または http://captive.Apple.com/hotspot- detect.html200
のHTTPステータスコードとSuccess
を含む本文を返す必要があります。
[〜#〜] note [〜#〜]:このアプローチは、中国全体のようにインターネットアクセスが制限されている地域では機能しません。 walled garden、およびほとんどのGoogle/Appleサービスがブロックまたはフィルタリングされます。これらの一部はブロックされない場合があります:
http://www.google.cn/generate_204
、http://g.cn/generate_204
、http://gstatic.com/generate_204
またはhttp://connectivitycheck.gstatic.com/generate_204
—これらはすべてgoogleに属しているため、動作が保証されていません。
別の可能な解決策は、HTTPS経由で接続し、ターゲット証明書を検査することです。壁に囲まれた庭園が実際にHTTPS経由でログインページを提供するのか、単に接続を切断するのかはわかりません。どちらの場合でも、目的地が期待したものではないことがわかるはずです。
もちろん、TLSと証明書チェックのオーバーヘッドもあります。残念ながら、これは認証された接続の価格です。
接続のリダイレクトを防ぐことはうまくいくと思います。
URL url = new URL(hostUrl));
HttpURLConnection httpConn = (HttpURLConnection)url.openConnection();
/* This line prevents redirects */
httpConn.setInstanceFollowRedirects( false );
httpConn.setAllowUserInteraction( false );
httpConn.setRequestMethod( "GET" );
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;
それがうまくいかない場合、それを行う唯一の方法は応答の本文をチェックすることだと思います。
これはAOSPのようにここで行うのが最適です: https://github.com/aosp-mirror/platform_frameworks_base/blob/6bebb8418ceecf44d2af40033870f3aabacfe36e/core/Java/Android/net/captiveportal/CaptivePortalProbeResult.Java# L61
private static final String GOOGLE_PING_URL = "http://google.com/generate_204";
private static final int SOCKET_TIMEOUT_MS = 10000;
public boolean isCaptivePortal () {
try {
URL url = new URL(GOOGLE_PING_URL);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
return (urlConnection.getResponseCode() != 204)
&& (urlConnection.getResponseCode() >= 200)
&& (urlConnection.getResponseCode() <= 399);
} catch (Exception e) {
// for any exception throw an exception saying check was unsuccesful
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
これはおそらくプロキシネットワークでは機能せず、AOSP urlのように高度なものを実行する必要があることに注意してください
既にretrofit
を使用している場合は、retrofit
で実行できます。 ping.htmlページを作成し、retrofitを使用してヘッドリクエストを送信し、httpクライアントが以下のように構成されていることを確認します。(followRedirects(false)
部分が最も重要な部分です)
private OkHttpClient getCheckInternetOkHttpClient() {
return new OkHttpClient.Builder()
.readTimeout(2L, TimeUnit.SECONDS)
.connectTimeout(2L, TimeUnit.SECONDS)
.followRedirects(false)
.build();
}
次に、以下のようにレトロフィットを構築します。
private InternetCheckApi getCheckInternetRetrofitApi() {
return (new Retrofit.Builder())
.baseUrl("[base url of your ping.html page]")
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.client(getCheckInternetOkHttpClient())
.build().create(InternetCheckApi.class);
}
internetCheckApi.classは次のようになります。
public interface InternetCheckApi {
@Headers({"Content-Typel: application/json"})
@HEAD("ping.html")
Call<Void> checkInternetConnectivity();
}
以下のように使用できます:
getCheckInternetOkHttpClient().checkInternetConnectivity().enqueue(new Callback<Void>() {
public void onResponse(Call<Void> call, Response<Void> response) {
if(response.code() == 200) {
//internet is available
} else {
//internet is not available
}
}
public void onFailure(Call<Void> call, Throwable t) {
//internet is not available
}
}
);
インターネットチェックHTTPクライアントは、メインのHTTPクライアントとは別にする必要があることに注意してください。
これはAndroid 4.2.2+バージョンで実装されました-彼らのアプローチは速くて面白いと思います:
CaptivePortalTracker.Javaは次のようにウォールドガーデンを検出します-www.google.com/generate_204に接続してみます-HTTPレスポンスが204であることを確認します
チェックに失敗した場合、私たちは壁に囲まれた庭にいます。
private boolean isCaptivePortal(InetAddress server) {
HttpURLConnection urlConnection = null;
if (!mIsCaptivePortalCheckEnabled) return false;
mUrl = "http://" + server.getHostAddress() + "/generate_204";
if (DBG) log("Checking " + mUrl);
try {
URL url = new URL(mUrl);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
// we got a valid response, but not from the real google
return urlConnection.getResponseCode() != 204;
} catch (IOException e) {
if (DBG) log("Probably not a portal: exception " + e);
return false;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}