CaptivePortalLoginActivity.java revision 2c02197bdd12378c7b2f8ee4bcaa625b2c564ede
1869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen/* 2869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Copyright (C) 2014 The Android Open Source Project 3869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * 4869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Licensed under the Apache License, Version 2.0 (the "License"); 5869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * you may not use this file except in compliance with the License. 6869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * You may obtain a copy of the License at 7869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * 8869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * http://www.apache.org/licenses/LICENSE-2.0 9869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * 10869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Unless required by applicable law or agreed to in writing, software 11869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * distributed under the License is distributed on an "AS IS" BASIS, 12869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * See the License for the specific language governing permissions and 14869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * limitations under the License. 15869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen */ 16869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 17869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenpackage com.android.captiveportallogin; 18869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 19869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.app.Activity; 2088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.app.LoadedApk; 2188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.content.Context; 22869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.content.Intent; 23869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.graphics.Bitmap; 2449e3edff5156f471819e4ea2a88994bca70bd870Paul Jensenimport android.net.CaptivePortal; 25869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.net.ConnectivityManager; 268df099df1516d23c113be3121635dcd34984a4a0Paul Jensenimport android.net.ConnectivityManager.NetworkCallback; 27869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.net.Network; 288df099df1516d23c113be3121635dcd34984a4a0Paul Jensenimport android.net.NetworkCapabilities; 298df099df1516d23c113be3121635dcd34984a4a0Paul Jensenimport android.net.NetworkRequest; 3088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.net.Proxy; 3188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.net.Uri; 32fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensenimport android.net.http.SslError; 33869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.os.Bundle; 34869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.provider.Settings; 3588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.util.ArrayMap; 3688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.util.Log; 3765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensenimport android.util.TypedValue; 38869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.view.Menu; 39869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.view.MenuItem; 40fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensenimport android.webkit.SslErrorHandler; 41869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebChromeClient; 42869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebSettings; 43869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebView; 44869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebViewClient; 458f333f19222ac9415152e31f10e0df2b571b0b77Paul Jensenimport android.widget.ProgressBar; 465344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensenimport android.widget.TextView; 47869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 48869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.io.IOException; 49869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.HttpURLConnection; 50869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.MalformedURLException; 51869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.URL; 52869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.lang.InterruptedException; 5388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport java.lang.reflect.Field; 5488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport java.lang.reflect.Method; 5565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensenimport java.util.Random; 56869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 57869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenpublic class CaptivePortalLoginActivity extends Activity { 587f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName(); 597f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi private static final boolean DBG = true; 607f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi 61869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private static final int SOCKET_TIMEOUT_MS = 10000; 62869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 6325a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS }; 64869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 657f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi private URL mUrl; 662c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi private String mUserAgent; 6725a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen private Network mNetwork; 6849e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen private CaptivePortal mCaptivePortal; 698df099df1516d23c113be3121635dcd34984a4a0Paul Jensen private NetworkCallback mNetworkCallback; 7025a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen private ConnectivityManager mCm; 7165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen private boolean mLaunchBrowser = false; 72e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen private MyWebViewClient mWebViewClient; 73869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 74869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 75869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen protected void onCreate(Bundle savedInstanceState) { 76869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen super.onCreate(savedInstanceState); 7725a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen mCm = ConnectivityManager.from(this); 7825a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK); 7949e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL); 802c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi mUserAgent = getIntent().getParcelableExtra( 812c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT); 827f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi mUrl = getUrl(); 837f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (mUrl == null) { 847f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi // getUrl() failed to parse the url provided in the intent: bail out in a way that 857f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi // at least provides network access. 867f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi done(Result.WANTED_AS_IS); 877f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi return; 887f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 897f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (DBG) { 907f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi Log.d(TAG, String.format("onCreate for %s", mUrl.toString())); 917f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 92869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 93e0bef71662d81caaaa0d7214fb0bef5d39996a69Paul Jensen // Also initializes proxy system properties. 9425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen mCm.bindProcessToNetwork(mNetwork); 9588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen 9688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Proxy system properties must be initialized before setContentView is called because 9788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // setContentView initializes the WebView logic which in turn reads the system properties. 9888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen setContentView(R.layout.activity_captive_portal_login); 9988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen 10088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen getActionBar().setDisplayShowHomeEnabled(false); 10188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen 1028df099df1516d23c113be3121635dcd34984a4a0Paul Jensen // Exit app if Network disappears. 10325a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork); 1048df099df1516d23c113be3121635dcd34984a4a0Paul Jensen if (networkCapabilities == null) { 1056a776c8317138cf4a3013addc1bd1a462f6dc1ebPaul Jensen finishAndRemoveTask(); 1068df099df1516d23c113be3121635dcd34984a4a0Paul Jensen return; 1078df099df1516d23c113be3121635dcd34984a4a0Paul Jensen } 1088df099df1516d23c113be3121635dcd34984a4a0Paul Jensen mNetworkCallback = new NetworkCallback() { 1098df099df1516d23c113be3121635dcd34984a4a0Paul Jensen @Override 1108df099df1516d23c113be3121635dcd34984a4a0Paul Jensen public void onLost(Network lostNetwork) { 11125a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED); 1128df099df1516d23c113be3121635dcd34984a4a0Paul Jensen } 1138df099df1516d23c113be3121635dcd34984a4a0Paul Jensen }; 1148df099df1516d23c113be3121635dcd34984a4a0Paul Jensen final NetworkRequest.Builder builder = new NetworkRequest.Builder(); 1158df099df1516d23c113be3121635dcd34984a4a0Paul Jensen for (int transportType : networkCapabilities.getTransportTypes()) { 1168df099df1516d23c113be3121635dcd34984a4a0Paul Jensen builder.addTransportType(transportType); 1178df099df1516d23c113be3121635dcd34984a4a0Paul Jensen } 11825a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen mCm.registerNetworkCallback(builder.build(), mNetworkCallback); 119869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 12088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen final WebView myWebView = (WebView) findViewById(R.id.webview); 12188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen myWebView.clearCache(true); 122869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen WebSettings webSettings = myWebView.getSettings(); 123869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen webSettings.setJavaScriptEnabled(true); 124b55bf38351fc06d267735e8e377d4049c2a7b5d3Lorenzo Colitti webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); 125e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen mWebViewClient = new MyWebViewClient(); 126e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen myWebView.setWebViewClient(mWebViewClient); 127869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen myWebView.setWebChromeClient(new MyWebChromeClient()); 12888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Start initial page load so WebView finishes loading proxy settings. 12988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Actual load of mUrl is initiated by MyWebViewClient. 13088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen myWebView.loadData("", "text/html", null); 13188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } 13288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen 13388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties. 13488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen private void setWebViewProxy() { 13588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen LoadedApk loadedApk = getApplication().mLoadedApk; 13688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen try { 13788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Field receiversField = LoadedApk.class.getDeclaredField("mReceivers"); 13888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen receiversField.setAccessible(true); 13988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk); 14088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen for (Object receiverMap : receivers.values()) { 14188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen for (Object rec : ((ArrayMap) receiverMap).keySet()) { 14288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Class clazz = rec.getClass(); 14388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen if (clazz.getName().contains("ProxyChangeListener")) { 14488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, 14588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Intent.class); 14688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); 14788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen onReceiveMethod.invoke(rec, getApplicationContext(), intent); 14888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Log.v(TAG, "Prompting WebView proxy reload."); 14988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } 15088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } 15188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } 15288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } catch (Exception e) { 15388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Log.e(TAG, "Exception while setting WebView proxy: " + e); 15488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } 155869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 156869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 15725a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen private void done(Result result) { 1587f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (DBG) { 1597f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi Log.d(TAG, String.format("Result %s for %s", result.name(), mUrl.toString())); 1607f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 16171b645fe9cb8106dfcbf025a3fd7f58698c051bbPaul Jensen if (mNetworkCallback != null) { 16225a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen mCm.unregisterNetworkCallback(mNetworkCallback); 163868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen mNetworkCallback = null; 16425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen } 16525a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen switch (result) { 16625a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen case DISMISSED: 16749e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen mCaptivePortal.reportCaptivePortalDismissed(); 16825a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen break; 16925a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen case UNWANTED: 17049e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen mCaptivePortal.ignoreNetwork(); 17125a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen break; 17225a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen case WANTED_AS_IS: 17349e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen mCaptivePortal.useNetwork(); 17425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen break; 17571b645fe9cb8106dfcbf025a3fd7f58698c051bbPaul Jensen } 1766a776c8317138cf4a3013addc1bd1a462f6dc1ebPaul Jensen finishAndRemoveTask(); 177869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 178869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 179869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 180869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public boolean onCreateOptionsMenu(Menu menu) { 181869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen getMenuInflater().inflate(R.menu.captive_portal_login, menu); 182869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen return true; 183869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 184869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 185869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 186b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen public void onBackPressed() { 187b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen WebView myWebView = (WebView) findViewById(R.id.webview); 188e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen if (myWebView.canGoBack() && mWebViewClient.allowBack()) { 189b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen myWebView.goBack(); 190b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen } else { 191b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen super.onBackPressed(); 192b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen } 193b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen } 194b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen 195b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen @Override 196869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public boolean onOptionsItemSelected(MenuItem item) { 1977f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi final Result result; 1987f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi final String action; 1997f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi final int id = item.getItemId(); 2007f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi switch (id) { 2017f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi case R.id.action_use_network: 2027f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi result = Result.WANTED_AS_IS; 2037f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi action = "USE_NETWORK"; 2047f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi break; 2057f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi case R.id.action_do_not_use_network: 2067f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi result = Result.UNWANTED; 2077f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi action = "DO_NOT_USE_NETWORK"; 2087f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi break; 2097f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi default: 2107f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi return super.onOptionsItemSelected(item); 211869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 2127f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (DBG) { 2137f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi Log.d(TAG, String.format("onOptionsItemSelect %s for %s", action, mUrl.toString())); 214869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 2157f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi done(result); 2167f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi return true; 217869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 218869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 219868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen @Override 220868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen public void onDestroy() { 221868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen super.onDestroy(); 222868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen if (mNetworkCallback != null) { 223868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen mCm.unregisterNetworkCallback(mNetworkCallback); 224868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen mNetworkCallback = null; 225868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen } 22665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen if (mLaunchBrowser) { 22765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // Give time for this network to become default. After 500ms just proceed. 22865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen for (int i = 0; i < 5; i++) { 22965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // TODO: This misses when mNetwork underlies a VPN. 23065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen if (mNetwork.equals(mCm.getActiveNetwork())) break; 23165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen try { 23265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen Thread.sleep(100); 23365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } catch (InterruptedException e) { 23465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } 23565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } 2367f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi final String url = mUrl.toString(); 2377f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (DBG) { 2387f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi Log.d(TAG, "starting activity with intent ACTION_VIEW for " + url); 2397f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 2407f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); 2417f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 2427f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 2437f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi 2447f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi private URL getUrl() { 2457f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL); 2467f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (url == null) { 2477f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi url = mCm.getCaptivePortalServerUrl(); 2487f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 2497f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi try { 2507f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi return new URL(url); 2517f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } catch (MalformedURLException e) { 2527f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi Log.e(TAG, "Invalid captive portal URL " + url); 25365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } 2547f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi return null; 255868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen } 256868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen 257869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private void testForCaptivePortal() { 2582c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi // TODO: reuse NetworkMonitor facilities for consistent captive portal detection. 259869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen new Thread(new Runnable() { 260869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public void run() { 261869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // Give time for captive portal to open. 262869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen try { 263869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen Thread.sleep(1000); 264869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } catch (InterruptedException e) { 265869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 266869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen HttpURLConnection urlConnection = null; 267869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen int httpResponseCode = 500; 268869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen try { 2692c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl); 270869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen urlConnection.setInstanceFollowRedirects(false); 271869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); 272869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); 273869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen urlConnection.setUseCaches(false); 2742c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi if (mUserAgent != null) { 2752c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi urlConnection.setRequestProperty("User-Agent", mUserAgent); 2762c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi } 277869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen urlConnection.getInputStream(); 278869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen httpResponseCode = urlConnection.getResponseCode(); 279869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } catch (IOException e) { 280869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } finally { 281869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen if (urlConnection != null) urlConnection.disconnect(); 282869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 283869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen if (httpResponseCode == 204) { 28425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen done(Result.DISMISSED); 285869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 286869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 287869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen }).start(); 288869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 289869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 290869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private class MyWebViewClient extends WebViewClient { 2915344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen private static final String INTERNAL_ASSETS = "file:///android_asset/"; 29265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen private final String mBrowserBailOutToken = Long.toString(new Random().nextLong()); 29365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // How many Android device-independent-pixels per scaled-pixel 29465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp) 29565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1, 29665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen getResources().getDisplayMetrics()) / 29765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, 29865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen getResources().getDisplayMetrics()); 299e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen private int mPagesLoaded; 300e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen 301e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // If we haven't finished cleaning up the history, don't allow going back. 302e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen public boolean allowBack() { 303e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen return mPagesLoaded > 1; 304e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen } 30588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen 306869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 307869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public void onPageStarted(WebView view, String url, Bitmap favicon) { 30865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen if (url.contains(mBrowserBailOutToken)) { 30965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen mLaunchBrowser = true; 31065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen done(Result.WANTED_AS_IS); 31165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen return; 31265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } 313e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // The first page load is used only to cause the WebView to 314e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // fetch the proxy settings. Don't update the URL bar, and 315e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // don't check if the captive portal is still there. 316e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen if (mPagesLoaded == 0) return; 317e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // For internally generated pages, leave URL bar listing prior URL as this is the URL 318e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // the page refers to. 319e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen if (!url.startsWith(INTERNAL_ASSETS)) { 320e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen final TextView myUrlBar = (TextView) findViewById(R.id.url_bar); 321e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen myUrlBar.setText(url); 322e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen } 323869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen testForCaptivePortal(); 324869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 325869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 326869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 327869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public void onPageFinished(WebView view, String url) { 328e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen mPagesLoaded++; 329e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen if (mPagesLoaded == 1) { 33088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Now that WebView has loaded at least one page we know it has read in the proxy 33188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // settings. Now prompt the WebView read the Network-specific proxy settings. 33288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen setWebViewProxy(); 33388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Load the real page. 3347f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi view.loadUrl(mUrl.toString()); 33588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen return; 336e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen } else if (mPagesLoaded == 2) { 337e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // Prevent going back to empty first page. 338e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen view.clearHistory(); 3395344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen } 340869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen testForCaptivePortal(); 341869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 342fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen 34365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // Convert Android device-independent-pixels (dp) to HTML size. 34465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen private String dp(int dp) { 34565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // HTML px's are scaled just like dp's, so just add "px" suffix. 34665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen return Integer.toString(dp) + "px"; 34765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } 34865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen 34965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // Convert Android scaled-pixels (sp) to HTML size. 35065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen private String sp(int sp) { 35165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // Convert sp to dp's. 35265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen float dp = sp * mDpPerSp; 35365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // Apply a scale factor to make things look right. 35465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen dp *= 1.3; 35565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // Convert dp's to HTML size. 35665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen return dp((int)dp); 35765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } 35865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen 359fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen // A web page consisting of a large broken lock icon to indicate SSL failure. 36065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen private final String SSL_ERROR_HTML = "<html><head><style>" + 36165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen "body { margin-left:" + dp(48) + "; margin-right:" + dp(48) + "; " + 36265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen "margin-top:" + dp(96) + "; background-color:#fafafa; }" + 36365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen "img { width:" + dp(48) + "; height:" + dp(48) + "; }" + 36465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen "div.warn { font-size:" + sp(16) + "; margin-top:" + dp(16) + "; " + 36565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen " opacity:0.87; line-height:1.28; }" + 36665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen "div.example { font-size:" + sp(14) + "; margin-top:" + dp(16) + "; " + 36765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen " opacity:0.54; line-height:1.21905; }" + 36865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen "a { font-size:" + sp(14) + "; text-decoration:none; text-transform:uppercase; " + 36965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen " margin-top:" + dp(24) + "; display:inline-block; color:#4285F4; " + 37065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen " height:" + dp(48) + "; font-weight:bold; }" + 37165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen "</style></head><body><p><img src=quantum_ic_warning_amber_96.png><br>" + 37265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen "<div class=warn>%s</div>" + 37365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen "<div class=example>%s</div>" + 37465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen "<a href=%s>%s</a></body></html>"; 375fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen 376fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen @Override 377fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { 37841ff524a6325feb8dc9d3d64545c5f2aeb964f34Paul Jensen Log.w(TAG, "SSL error (error: " + error.getPrimaryError() + " host: " + 37941ff524a6325feb8dc9d3d64545c5f2aeb964f34Paul Jensen // Only show host to avoid leaking private info. 38041ff524a6325feb8dc9d3d64545c5f2aeb964f34Paul Jensen Uri.parse(error.getUrl()).getHost() + " certificate: " + 38141ff524a6325feb8dc9d3d64545c5f2aeb964f34Paul Jensen error.getCertificate() + "); displaying SSL warning."); 38265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen final String html = String.format(SSL_ERROR_HTML, getString(R.string.ssl_error_warning), 38365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen getString(R.string.ssl_error_example), mBrowserBailOutToken, 38465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen getString(R.string.ssl_error_continue)); 38565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen view.loadDataWithBaseURL(INTERNAL_ASSETS, html, "text/HTML", "UTF-8", null); 386fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen } 387fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen 388fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen @Override 389fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen public boolean shouldOverrideUrlLoading (WebView view, String url) { 390fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen if (url.startsWith("tel:")) { 391fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url))); 392fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen return true; 393fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen } 394fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen return false; 395fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen } 396869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 397869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 398869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private class MyWebChromeClient extends WebChromeClient { 399869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 400869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public void onProgressChanged(WebView view, int newProgress) { 4015344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen final ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.progress_bar); 4028f333f19222ac9415152e31f10e0df2b571b0b77Paul Jensen myProgressBar.setProgress(newProgress); 403869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 404869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 405869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen} 406