CaptivePortalLoginActivity.java revision 94bc48f7bbff4772de967bcfc3effd4f710503c2
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; 293a222974e9cdbb120d22c439580401a5d63b51b2Hugo Benichiimport android.net.NetworkInfo; 308df099df1516d23c113be3121635dcd34984a4a0Paul Jensenimport android.net.NetworkRequest; 3188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.net.Proxy; 3288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.net.Uri; 33fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensenimport android.net.http.SslError; 3412df465997c1be51c6802acad2dcf20f010c3576Hugo Benichiimport android.os.Build; 35869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.os.Bundle; 36869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.provider.Settings; 3794bc48f7bbff4772de967bcfc3effd4f710503c2Chalard Jeanimport android.support.v4.widget.SwipeRefreshLayout; 3888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.util.ArrayMap; 3988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.util.Log; 4065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensenimport android.util.TypedValue; 4104d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichiimport android.util.SparseArray; 42869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.view.Menu; 43869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.view.MenuItem; 44a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichiimport android.view.View; 45fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensenimport android.webkit.SslErrorHandler; 46869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebChromeClient; 47869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebSettings; 48869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebView; 4904d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichiimport android.webkit.WebView; 50869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebViewClient; 518f333f19222ac9415152e31f10e0df2b571b0b77Paul Jensenimport android.widget.ProgressBar; 525344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensenimport android.widget.TextView; 53869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 549e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichiimport com.android.internal.logging.MetricsLogger; 559e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichiimport com.android.internal.logging.nano.MetricsProto.MetricsEvent; 569e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi 57869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.io.IOException; 58869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.HttpURLConnection; 59869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.MalformedURLException; 60869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.URL; 61869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.lang.InterruptedException; 6288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport java.lang.reflect.Field; 6388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport java.lang.reflect.Method; 6412df465997c1be51c6802acad2dcf20f010c3576Hugo Benichiimport java.util.Objects; 6565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensenimport java.util.Random; 66a173a63a6cf9c94c511d14d75648f55525ce7006Hugo Benichiimport java.util.concurrent.atomic.AtomicBoolean; 67869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 68869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenpublic class CaptivePortalLoginActivity extends Activity { 697f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName(); 707f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi private static final boolean DBG = true; 7160d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi private static final boolean VDBG = false; 727f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi 73869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private static final int SOCKET_TIMEOUT_MS = 10000; 74869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 759e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi private enum Result { 769e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi DISMISSED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED), 779e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi UNWANTED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED), 789e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi WANTED_AS_IS(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS); 799e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi 809e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi final int metricsEvent; 819e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi Result(int metricsEvent) { this.metricsEvent = metricsEvent; } 829e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi }; 83869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 847f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi private URL mUrl; 852c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi private String mUserAgent; 8625a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen private Network mNetwork; 8749e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen private CaptivePortal mCaptivePortal; 888df099df1516d23c113be3121635dcd34984a4a0Paul Jensen private NetworkCallback mNetworkCallback; 8925a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen private ConnectivityManager mCm; 9065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen private boolean mLaunchBrowser = false; 91e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen private MyWebViewClient mWebViewClient; 9294bc48f7bbff4772de967bcfc3effd4f710503c2Chalard Jean private SwipeRefreshLayout mSwipeRefreshLayout; 93a173a63a6cf9c94c511d14d75648f55525ce7006Hugo Benichi // Ensures that done() happens once exactly, handling concurrent callers with atomic operations. 94a173a63a6cf9c94c511d14d75648f55525ce7006Hugo Benichi private final AtomicBoolean isDone = new AtomicBoolean(false); 95869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 96869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 97869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen protected void onCreate(Bundle savedInstanceState) { 98869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen super.onCreate(savedInstanceState); 999e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi 1009e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi logMetricsEvent(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY); 1019e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi 10225a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen mCm = ConnectivityManager.from(this); 10325a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK); 10449e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL); 105ec88fd6708c14515f75d0f9de24e505e62fc3550Hugo Benichi mUserAgent = 106ec88fd6708c14515f75d0f9de24e505e62fc3550Hugo Benichi getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT); 1077f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi mUrl = getUrl(); 1087f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (mUrl == null) { 1097f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi // getUrl() failed to parse the url provided in the intent: bail out in a way that 1107f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi // at least provides network access. 1117f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi done(Result.WANTED_AS_IS); 1127f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi return; 1137f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 1147f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (DBG) { 1157f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi Log.d(TAG, String.format("onCreate for %s", mUrl.toString())); 1167f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 117869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 118e0bef71662d81caaaa0d7214fb0bef5d39996a69Paul Jensen // Also initializes proxy system properties. 11925a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen mCm.bindProcessToNetwork(mNetwork); 12088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen 12188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Proxy system properties must be initialized before setContentView is called because 12288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // setContentView initializes the WebView logic which in turn reads the system properties. 12388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen setContentView(R.layout.activity_captive_portal_login); 12488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen 1258df099df1516d23c113be3121635dcd34984a4a0Paul Jensen // Exit app if Network disappears. 12625a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork); 1278df099df1516d23c113be3121635dcd34984a4a0Paul Jensen if (networkCapabilities == null) { 1286a776c8317138cf4a3013addc1bd1a462f6dc1ebPaul Jensen finishAndRemoveTask(); 1298df099df1516d23c113be3121635dcd34984a4a0Paul Jensen return; 1308df099df1516d23c113be3121635dcd34984a4a0Paul Jensen } 1318df099df1516d23c113be3121635dcd34984a4a0Paul Jensen mNetworkCallback = new NetworkCallback() { 1328df099df1516d23c113be3121635dcd34984a4a0Paul Jensen @Override 1338df099df1516d23c113be3121635dcd34984a4a0Paul Jensen public void onLost(Network lostNetwork) { 13425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED); 1358df099df1516d23c113be3121635dcd34984a4a0Paul Jensen } 1368df099df1516d23c113be3121635dcd34984a4a0Paul Jensen }; 1378df099df1516d23c113be3121635dcd34984a4a0Paul Jensen final NetworkRequest.Builder builder = new NetworkRequest.Builder(); 1388df099df1516d23c113be3121635dcd34984a4a0Paul Jensen for (int transportType : networkCapabilities.getTransportTypes()) { 1398df099df1516d23c113be3121635dcd34984a4a0Paul Jensen builder.addTransportType(transportType); 1408df099df1516d23c113be3121635dcd34984a4a0Paul Jensen } 14125a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen mCm.registerNetworkCallback(builder.build(), mNetworkCallback); 142869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 143a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi getActionBar().setDisplayShowHomeEnabled(false); 144a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi getActionBar().setElevation(0); // remove shadow 145a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi getActionBar().setTitle(getHeaderTitle()); 146a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi getActionBar().setSubtitle(""); 147a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi 148a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi final WebView webview = getWebview(); 149a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi webview.clearCache(true); 150a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi WebSettings webSettings = webview.getSettings(); 151869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen webSettings.setJavaScriptEnabled(true); 152b55bf38351fc06d267735e8e377d4049c2a7b5d3Lorenzo Colitti webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); 15394c5fb342bbb04bcf099807752243c84b26b9a9bHugo Benichi webSettings.setUseWideViewPort(true); 15494c5fb342bbb04bcf099807752243c84b26b9a9bHugo Benichi webSettings.setLoadWithOverviewMode(true); 15594c5fb342bbb04bcf099807752243c84b26b9a9bHugo Benichi webSettings.setSupportZoom(true); 15694c5fb342bbb04bcf099807752243c84b26b9a9bHugo Benichi webSettings.setBuiltInZoomControls(true); 15794c5fb342bbb04bcf099807752243c84b26b9a9bHugo Benichi webSettings.setDisplayZoomControls(false); 158e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen mWebViewClient = new MyWebViewClient(); 159a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi webview.setWebViewClient(mWebViewClient); 160a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi webview.setWebChromeClient(new MyWebChromeClient()); 16188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Start initial page load so WebView finishes loading proxy settings. 16288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Actual load of mUrl is initiated by MyWebViewClient. 163a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi webview.loadData("", "text/html", null); 16494bc48f7bbff4772de967bcfc3effd4f710503c2Chalard Jean 16594bc48f7bbff4772de967bcfc3effd4f710503c2Chalard Jean mSwipeRefreshLayout = findViewById(R.id.swipe_refresh); 16694bc48f7bbff4772de967bcfc3effd4f710503c2Chalard Jean mSwipeRefreshLayout.setOnRefreshListener(() -> { 16794bc48f7bbff4772de967bcfc3effd4f710503c2Chalard Jean webview.reload(); 16894bc48f7bbff4772de967bcfc3effd4f710503c2Chalard Jean mSwipeRefreshLayout.setRefreshing(true); 16994bc48f7bbff4772de967bcfc3effd4f710503c2Chalard Jean }); 17094bc48f7bbff4772de967bcfc3effd4f710503c2Chalard Jean 17188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } 17288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen 17388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties. 17488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen private void setWebViewProxy() { 17588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen LoadedApk loadedApk = getApplication().mLoadedApk; 17688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen try { 17788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Field receiversField = LoadedApk.class.getDeclaredField("mReceivers"); 17888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen receiversField.setAccessible(true); 17988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk); 18088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen for (Object receiverMap : receivers.values()) { 18188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen for (Object rec : ((ArrayMap) receiverMap).keySet()) { 18288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Class clazz = rec.getClass(); 18388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen if (clazz.getName().contains("ProxyChangeListener")) { 18488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, 18588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Intent.class); 18688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); 18788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen onReceiveMethod.invoke(rec, getApplicationContext(), intent); 18888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Log.v(TAG, "Prompting WebView proxy reload."); 18988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } 19088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } 19188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } 19288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } catch (Exception e) { 19388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen Log.e(TAG, "Exception while setting WebView proxy: " + e); 19488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen } 195869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 196869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 19725a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen private void done(Result result) { 198a173a63a6cf9c94c511d14d75648f55525ce7006Hugo Benichi if (isDone.getAndSet(true)) { 199a173a63a6cf9c94c511d14d75648f55525ce7006Hugo Benichi // isDone was already true: done() already called 200a173a63a6cf9c94c511d14d75648f55525ce7006Hugo Benichi return; 201a173a63a6cf9c94c511d14d75648f55525ce7006Hugo Benichi } 2027f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (DBG) { 2037f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi Log.d(TAG, String.format("Result %s for %s", result.name(), mUrl.toString())); 2047f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 2059e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi logMetricsEvent(result.metricsEvent); 20625a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen switch (result) { 20725a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen case DISMISSED: 20849e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen mCaptivePortal.reportCaptivePortalDismissed(); 20925a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen break; 21025a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen case UNWANTED: 21149e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen mCaptivePortal.ignoreNetwork(); 21225a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen break; 21325a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen case WANTED_AS_IS: 21449e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen mCaptivePortal.useNetwork(); 21525a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen break; 21671b645fe9cb8106dfcbf025a3fd7f58698c051bbPaul Jensen } 2176a776c8317138cf4a3013addc1bd1a462f6dc1ebPaul Jensen finishAndRemoveTask(); 218869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 219869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 220869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 221869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public boolean onCreateOptionsMenu(Menu menu) { 222869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen getMenuInflater().inflate(R.menu.captive_portal_login, menu); 223869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen return true; 224869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 225869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 226869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 227b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen public void onBackPressed() { 22851efddbd3bb304de2dd47fa8cd1114ac555958bbAlan Viverette WebView myWebView = findViewById(R.id.webview); 229e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen if (myWebView.canGoBack() && mWebViewClient.allowBack()) { 230b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen myWebView.goBack(); 231b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen } else { 232b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen super.onBackPressed(); 233b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen } 234b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen } 235b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen 236b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen @Override 237869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public boolean onOptionsItemSelected(MenuItem item) { 2387f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi final Result result; 2397f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi final String action; 2407f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi final int id = item.getItemId(); 2417f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi switch (id) { 2427f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi case R.id.action_use_network: 2437f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi result = Result.WANTED_AS_IS; 2447f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi action = "USE_NETWORK"; 2457f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi break; 2467f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi case R.id.action_do_not_use_network: 2477f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi result = Result.UNWANTED; 2487f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi action = "DO_NOT_USE_NETWORK"; 2497f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi break; 2507f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi default: 2517f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi return super.onOptionsItemSelected(item); 252869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 2537f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (DBG) { 2547f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi Log.d(TAG, String.format("onOptionsItemSelect %s for %s", action, mUrl.toString())); 255869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 2567f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi done(result); 2577f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi return true; 258869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 259869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 260868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen @Override 261868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen public void onDestroy() { 262868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen super.onDestroy(); 263868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen if (mNetworkCallback != null) { 264a173a63a6cf9c94c511d14d75648f55525ce7006Hugo Benichi // mNetworkCallback is not null if mUrl is not null. 265868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen mCm.unregisterNetworkCallback(mNetworkCallback); 266868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen } 26765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen if (mLaunchBrowser) { 26865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // Give time for this network to become default. After 500ms just proceed. 26965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen for (int i = 0; i < 5; i++) { 27065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // TODO: This misses when mNetwork underlies a VPN. 27165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen if (mNetwork.equals(mCm.getActiveNetwork())) break; 27265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen try { 27365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen Thread.sleep(100); 27465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } catch (InterruptedException e) { 27565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } 27665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } 2777f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi final String url = mUrl.toString(); 2787f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (DBG) { 2797f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi Log.d(TAG, "starting activity with intent ACTION_VIEW for " + url); 2807f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 2817f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); 2827f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 2837f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 2847f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi 2857f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi private URL getUrl() { 2867f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL); 2877f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi if (url == null) { 2887f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi url = mCm.getCaptivePortalServerUrl(); 2897f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } 290a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi return makeURL(url); 291a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi } 292a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi 293a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi private static URL makeURL(String url) { 2947f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi try { 2957f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi return new URL(url); 2967f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi } catch (MalformedURLException e) { 297a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi Log.e(TAG, "Invalid URL " + url); 29865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } 2997f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi return null; 300868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen } 301868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen 30212df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi private static String host(URL url) { 30312df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi if (url == null) { 30412df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi return null; 30512df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi } 30612df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi return url.getHost(); 30712df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi } 30812df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi 30912df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi private static String sanitizeURL(URL url) { 31012df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi // In non-Debug build, only show host to avoid leaking private info. 31112df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi return Build.IS_DEBUGGABLE ? Objects.toString(url) : host(url); 31212df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi } 31312df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi 314869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private void testForCaptivePortal() { 3152c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi // TODO: reuse NetworkMonitor facilities for consistent captive portal detection. 316869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen new Thread(new Runnable() { 317869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public void run() { 318869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // Give time for captive portal to open. 319869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen try { 320869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen Thread.sleep(1000); 321869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } catch (InterruptedException e) { 322869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 323869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen HttpURLConnection urlConnection = null; 324869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen int httpResponseCode = 500; 325869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen try { 3262c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl); 327869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen urlConnection.setInstanceFollowRedirects(false); 328869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); 329869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); 330869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen urlConnection.setUseCaches(false); 3312c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi if (mUserAgent != null) { 3322c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi urlConnection.setRequestProperty("User-Agent", mUserAgent); 3332c02197bdd12378c7b2f8ee4bcaa625b2c564edeHugo Benichi } 334ec88fd6708c14515f75d0f9de24e505e62fc3550Hugo Benichi // cannot read request header after connection 335ec88fd6708c14515f75d0f9de24e505e62fc3550Hugo Benichi String requestHeader = urlConnection.getRequestProperties().toString(); 336ec88fd6708c14515f75d0f9de24e505e62fc3550Hugo Benichi 337869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen urlConnection.getInputStream(); 338869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen httpResponseCode = urlConnection.getResponseCode(); 339ec88fd6708c14515f75d0f9de24e505e62fc3550Hugo Benichi if (DBG) { 340ec88fd6708c14515f75d0f9de24e505e62fc3550Hugo Benichi Log.d(TAG, "probe at " + mUrl + 341ec88fd6708c14515f75d0f9de24e505e62fc3550Hugo Benichi " ret=" + httpResponseCode + 342ec88fd6708c14515f75d0f9de24e505e62fc3550Hugo Benichi " request=" + requestHeader + 343ec88fd6708c14515f75d0f9de24e505e62fc3550Hugo Benichi " headers=" + urlConnection.getHeaderFields()); 344ec88fd6708c14515f75d0f9de24e505e62fc3550Hugo Benichi } 345869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } catch (IOException e) { 346869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } finally { 347869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen if (urlConnection != null) urlConnection.disconnect(); 348869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 349869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen if (httpResponseCode == 204) { 35025a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen done(Result.DISMISSED); 351869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 352869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 353869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen }).start(); 354869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 355869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 356869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private class MyWebViewClient extends WebViewClient { 3575344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen private static final String INTERNAL_ASSETS = "file:///android_asset/"; 35860d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi 35965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen private final String mBrowserBailOutToken = Long.toString(new Random().nextLong()); 36065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // How many Android device-independent-pixels per scaled-pixel 36165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp) 36265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1, 36365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen getResources().getDisplayMetrics()) / 36465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, 36565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen getResources().getDisplayMetrics()); 366e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen private int mPagesLoaded; 36712df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi // the host of the page that this webview is currently loading. Can be null when undefined. 36812df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi private String mHostname; 369e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen 370e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // If we haven't finished cleaning up the history, don't allow going back. 371e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen public boolean allowBack() { 372e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen return mPagesLoaded > 1; 373e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen } 37488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen 375869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 37612df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi public void onPageStarted(WebView view, String urlString, Bitmap favicon) { 37712df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi if (urlString.contains(mBrowserBailOutToken)) { 37865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen mLaunchBrowser = true; 37965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen done(Result.WANTED_AS_IS); 38065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen return; 38165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } 382e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // The first page load is used only to cause the WebView to 383e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // fetch the proxy settings. Don't update the URL bar, and 384e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // don't check if the captive portal is still there. 38512df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi if (mPagesLoaded == 0) { 38612df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi return; 38712df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi } 38812df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi final URL url = makeURL(urlString); 38904d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi Log.d(TAG, "onPageStarted: " + sanitizeURL(url)); 39012df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi mHostname = host(url); 391e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // For internally generated pages, leave URL bar listing prior URL as this is the URL 392e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // the page refers to. 39312df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi if (!urlString.startsWith(INTERNAL_ASSETS)) { 39412df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi String subtitle = (url != null) ? getHeaderSubtitle(url) : urlString; 39512df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi getActionBar().setSubtitle(subtitle); 396e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen } 397a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi getProgressBar().setVisibility(View.VISIBLE); 398869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen testForCaptivePortal(); 399869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 400869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 401869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 402869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public void onPageFinished(WebView view, String url) { 403e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen mPagesLoaded++; 404a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi getProgressBar().setVisibility(View.INVISIBLE); 40594bc48f7bbff4772de967bcfc3effd4f710503c2Chalard Jean mSwipeRefreshLayout.setRefreshing(false); 406e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen if (mPagesLoaded == 1) { 40788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Now that WebView has loaded at least one page we know it has read in the proxy 40888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // settings. Now prompt the WebView read the Network-specific proxy settings. 40988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen setWebViewProxy(); 41088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen // Load the real page. 4117f086e162b9000fd471f3450ae53fe1261f58993Hugo Benichi view.loadUrl(mUrl.toString()); 41288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen return; 413e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen } else if (mPagesLoaded == 2) { 414e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen // Prevent going back to empty first page. 41597640400c4aa6bf05d5606ea749b54ad63de97cdsusnata // Fix for missing focus, see b/62449959 for details. Remove it once we get a 41697640400c4aa6bf05d5606ea749b54ad63de97cdsusnata // newer version of WebView (60.x.y). 41797640400c4aa6bf05d5606ea749b54ad63de97cdsusnata view.requestFocus(); 418e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen view.clearHistory(); 4195344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen } 420869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen testForCaptivePortal(); 421869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 422fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen 42365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // Convert Android scaled-pixels (sp) to HTML size. 42465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen private String sp(int sp) { 42565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // Convert sp to dp's. 42665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen float dp = sp * mDpPerSp; 42765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // Apply a scale factor to make things look right. 42865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen dp *= 1.3; 42965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen // Convert dp's to HTML size. 43060d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi // HTML px's are scaled just like dp's, so just add "px" suffix. 43160d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi return Integer.toString((int)dp) + "px"; 43265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen } 43365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen 434fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen // A web page consisting of a large broken lock icon to indicate SSL failure. 435fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen 436fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen @Override 437fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { 43812df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi final URL url = makeURL(error.getUrl()); 43912df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi final String host = host(url); 44012df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi Log.d(TAG, String.format("SSL error: %s, url: %s, certificate: %s", 44104d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi sslErrorName(error), sanitizeURL(url), error.getCertificate())); 44212df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi if (url == null || !Objects.equals(host, mHostname)) { 44312df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi // Ignore ssl errors for resources coming from a different hostname than the page 44412df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi // that we are currently loading, and only cancel the request. 44512df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi handler.cancel(); 44612df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi return; 44712df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi } 4489e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR); 44960d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi final String sslErrorPage = makeSslErrorPage(); 45060d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi view.loadDataWithBaseURL(INTERNAL_ASSETS, sslErrorPage, "text/HTML", "UTF-8", null); 45160d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi } 45260d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi 45360d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi private String makeSslErrorPage() { 45460d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi final String warningMsg = getString(R.string.ssl_error_warning); 45560d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi final String exampleMsg = getString(R.string.ssl_error_example); 45660d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi final String continueMsg = getString(R.string.ssl_error_continue); 45760d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi return String.join("\n", 45860d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi "<html>", 45960d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi "<head>", 46060d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">", 46160d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " <style>", 46260d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " body {", 46360d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " background-color:#fafafa;", 46460d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " margin:auto;", 46560d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " width:80%;", 46660d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " margin-top: 96px", 46760d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " }", 46860d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " img {", 46960d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " height:48px;", 47060d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " width:48px;", 47160d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " }", 47260d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " div.warn {", 47360d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " font-size:" + sp(16) + ";", 47460d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " line-height:1.28;", 47560d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " margin-top:16px;", 47660d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " opacity:0.87;", 47760d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " }", 47860d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " div.example {", 47960d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " font-size:" + sp(14) + ";", 48060d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " line-height:1.21905;", 48160d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " margin-top:16px;", 48260d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " opacity:0.54;", 48360d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " }", 48460d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " a {", 48560d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " color:#4285F4;", 48660d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " display:inline-block;", 48760d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " font-size:" + sp(14) + ";", 48860d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " font-weight:bold;", 48960d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " height:48px;", 49060d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " margin-top:24px;", 49160d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " text-decoration:none;", 49260d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " text-transform:uppercase;", 49360d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " }", 49460d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " </style>", 49560d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi "</head>", 49660d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi "<body>", 49760d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " <p><img src=quantum_ic_warning_amber_96.png><br>", 49860d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " <div class=warn>" + warningMsg + "</div>", 49960d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " <div class=example>" + exampleMsg + "</div>", 50060d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi " <a href=" + mBrowserBailOutToken + ">" + continueMsg + "</a>", 50160d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi "</body>", 50260d5f46d89038a6a46c38ac0e57d0762a67732cbHugo Benichi "</html>"); 503fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen } 504fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen 505fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen @Override 506fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen public boolean shouldOverrideUrlLoading (WebView view, String url) { 507fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen if (url.startsWith("tel:")) { 508fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url))); 509fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen return true; 510fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen } 511fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen return false; 512fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen } 513869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 514869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 515869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private class MyWebChromeClient extends WebChromeClient { 516869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 517869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public void onProgressChanged(WebView view, int newProgress) { 518a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi getProgressBar().setProgress(newProgress); 519a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi } 520a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi } 521a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi 522a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi private ProgressBar getProgressBar() { 523a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi return findViewById(R.id.progress_bar); 524a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi } 525a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi 526a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi private WebView getWebview() { 527a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi return findViewById(R.id.webview); 528a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi } 529a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi 530a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi private String getHeaderTitle() { 5313a222974e9cdbb120d22c439580401a5d63b51b2Hugo Benichi NetworkInfo info = mCm.getNetworkInfo(mNetwork); 5323a222974e9cdbb120d22c439580401a5d63b51b2Hugo Benichi if (info == null) { 5333a222974e9cdbb120d22c439580401a5d63b51b2Hugo Benichi return getString(R.string.action_bar_label); 5343a222974e9cdbb120d22c439580401a5d63b51b2Hugo Benichi } 5353a222974e9cdbb120d22c439580401a5d63b51b2Hugo Benichi NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork); 5363a222974e9cdbb120d22c439580401a5d63b51b2Hugo Benichi if (!nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { 5373a222974e9cdbb120d22c439580401a5d63b51b2Hugo Benichi return getString(R.string.action_bar_label); 5383a222974e9cdbb120d22c439580401a5d63b51b2Hugo Benichi } 5393a222974e9cdbb120d22c439580401a5d63b51b2Hugo Benichi return getString(R.string.action_bar_title, info.getExtraInfo().replaceAll("^\"|\"$", "")); 540a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi } 541a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi 54212df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi private String getHeaderSubtitle(URL url) { 54312df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi String host = host(url); 544a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi final String https = "https"; 545a206649a6f66f16cc56db2f4e32b846d9b03501cHugo Benichi if (https.equals(url.getProtocol())) { 54612df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi return https + "://" + host; 547869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 54812df465997c1be51c6802acad2dcf20f010c3576Hugo Benichi return host; 549869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 5509e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi 5519e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi private void logMetricsEvent(int event) { 5529e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi MetricsLogger.action(this, event, getPackageName()); 5539e8ab43ab22555acff9fefca2ed433425c92fb87Hugo Benichi } 55404d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi 55504d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi private static final SparseArray<String> SSL_ERRORS = new SparseArray<>(); 55604d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi static { 55704d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi SSL_ERRORS.put(SslError.SSL_NOTYETVALID, "SSL_NOTYETVALID"); 55804d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi SSL_ERRORS.put(SslError.SSL_EXPIRED, "SSL_EXPIRED"); 55904d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi SSL_ERRORS.put(SslError.SSL_IDMISMATCH, "SSL_IDMISMATCH"); 56004d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi SSL_ERRORS.put(SslError.SSL_UNTRUSTED, "SSL_UNTRUSTED"); 56104d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi SSL_ERRORS.put(SslError.SSL_DATE_INVALID, "SSL_DATE_INVALID"); 56204d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi SSL_ERRORS.put(SslError.SSL_INVALID, "SSL_INVALID"); 56304d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi } 56404d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi 56504d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi private static String sslErrorName(SslError error) { 56604d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi return SSL_ERRORS.get(error.getPrimaryError(), "UNKNOWN"); 56704d78601ab0bbf7deee9ef97526fd9d45587aec1Hugo Benichi } 568869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen} 569