Home | History | Annotate | Download | only in captiveportallogin
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.captiveportallogin;
     18 
     19 import android.app.Activity;
     20 import android.app.LoadedApk;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.graphics.Bitmap;
     24 import android.net.ConnectivityManager;
     25 import android.net.ConnectivityManager.NetworkCallback;
     26 import android.net.LinkProperties;
     27 import android.net.Network;
     28 import android.net.NetworkCapabilities;
     29 import android.net.NetworkRequest;
     30 import android.net.Proxy;
     31 import android.net.ProxyInfo;
     32 import android.net.Uri;
     33 import android.os.Bundle;
     34 import android.provider.Settings;
     35 import android.provider.Settings.Global;
     36 import android.util.ArrayMap;
     37 import android.util.Log;
     38 import android.view.Menu;
     39 import android.view.MenuItem;
     40 import android.view.View;
     41 import android.view.Window;
     42 import android.webkit.WebChromeClient;
     43 import android.webkit.WebSettings;
     44 import android.webkit.WebView;
     45 import android.webkit.WebViewClient;
     46 import android.widget.ProgressBar;
     47 
     48 import java.io.IOException;
     49 import java.net.HttpURLConnection;
     50 import java.net.MalformedURLException;
     51 import java.net.URL;
     52 import java.lang.InterruptedException;
     53 import java.lang.reflect.Field;
     54 import java.lang.reflect.Method;
     55 
     56 public class CaptivePortalLoginActivity extends Activity {
     57     private static final String TAG = "CaptivePortalLogin";
     58     private static final String DEFAULT_SERVER = "clients3.google.com";
     59     private static final int SOCKET_TIMEOUT_MS = 10000;
     60 
     61     // Keep this in sync with NetworkMonitor.
     62     // Intent broadcast to ConnectivityService indicating sign-in is complete.
     63     // Extras:
     64     //     EXTRA_TEXT       = netId
     65     //     LOGGED_IN_RESULT = "1" if we should use network, "0" if not.
     66     private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
     67             "android.net.netmon.captive_portal_logged_in";
     68     private static final String LOGGED_IN_RESULT = "result";
     69 
     70     private URL mURL;
     71     private int mNetId;
     72     private NetworkCallback mNetworkCallback;
     73 
     74     @Override
     75     protected void onCreate(Bundle savedInstanceState) {
     76         super.onCreate(savedInstanceState);
     77 
     78         String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
     79         if (server == null) server = DEFAULT_SERVER;
     80         try {
     81             mURL = new URL("http://" + server + "/generate_204");
     82         } catch (MalformedURLException e) {
     83             done(true);
     84         }
     85 
     86         mNetId = Integer.parseInt(getIntent().getStringExtra(Intent.EXTRA_TEXT));
     87         final Network network = new Network(mNetId);
     88         ConnectivityManager.setProcessDefaultNetwork(network);
     89 
     90         // Set HTTP proxy system properties to those of the selected Network.
     91         final LinkProperties lp = ConnectivityManager.from(this).getLinkProperties(network);
     92         if (lp != null) {
     93             final ProxyInfo proxyInfo = lp.getHttpProxy();
     94             String host = "";
     95             String port = "";
     96             String exclList = "";
     97             Uri pacFileUrl = Uri.EMPTY;
     98             if (proxyInfo != null) {
     99                 host = proxyInfo.getHost();
    100                 port = Integer.toString(proxyInfo.getPort());
    101                 exclList = proxyInfo.getExclusionListAsString();
    102                 pacFileUrl = proxyInfo.getPacFileUrl();
    103             }
    104             Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
    105             Log.v(TAG, "Set proxy system properties to " + proxyInfo);
    106         }
    107 
    108         // Proxy system properties must be initialized before setContentView is called because
    109         // setContentView initializes the WebView logic which in turn reads the system properties.
    110         setContentView(R.layout.activity_captive_portal_login);
    111 
    112         getActionBar().setDisplayShowHomeEnabled(false);
    113 
    114         // Exit app if Network disappears.
    115         final NetworkCapabilities networkCapabilities =
    116                 ConnectivityManager.from(this).getNetworkCapabilities(network);
    117         if (networkCapabilities == null) {
    118             finish();
    119             return;
    120         }
    121         mNetworkCallback = new NetworkCallback() {
    122             @Override
    123             public void onLost(Network lostNetwork) {
    124                 if (network.equals(lostNetwork)) done(false);
    125             }
    126         };
    127         final NetworkRequest.Builder builder = new NetworkRequest.Builder();
    128         for (int transportType : networkCapabilities.getTransportTypes()) {
    129             builder.addTransportType(transportType);
    130         }
    131         ConnectivityManager.from(this).registerNetworkCallback(builder.build(), mNetworkCallback);
    132 
    133         final WebView myWebView = (WebView) findViewById(R.id.webview);
    134         myWebView.clearCache(true);
    135         WebSettings webSettings = myWebView.getSettings();
    136         webSettings.setJavaScriptEnabled(true);
    137         myWebView.setWebViewClient(new MyWebViewClient());
    138         myWebView.setWebChromeClient(new MyWebChromeClient());
    139         // Start initial page load so WebView finishes loading proxy settings.
    140         // Actual load of mUrl is initiated by MyWebViewClient.
    141         myWebView.loadData("", "text/html", null);
    142     }
    143 
    144     // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties.
    145     private void setWebViewProxy() {
    146         LoadedApk loadedApk = getApplication().mLoadedApk;
    147         try {
    148             Field receiversField = LoadedApk.class.getDeclaredField("mReceivers");
    149             receiversField.setAccessible(true);
    150             ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
    151             for (Object receiverMap : receivers.values()) {
    152                 for (Object rec : ((ArrayMap) receiverMap).keySet()) {
    153                     Class clazz = rec.getClass();
    154                     if (clazz.getName().contains("ProxyChangeListener")) {
    155                         Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class,
    156                                 Intent.class);
    157                         Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
    158                         onReceiveMethod.invoke(rec, getApplicationContext(), intent);
    159                         Log.v(TAG, "Prompting WebView proxy reload.");
    160                     }
    161                 }
    162             }
    163         } catch (Exception e) {
    164             Log.e(TAG, "Exception while setting WebView proxy: " + e);
    165         }
    166     }
    167 
    168     private void done(boolean use_network) {
    169         ConnectivityManager.from(this).unregisterNetworkCallback(mNetworkCallback);
    170         Intent intent = new Intent(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
    171         intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetId));
    172         intent.putExtra(LOGGED_IN_RESULT, use_network ? "1" : "0");
    173         sendBroadcast(intent);
    174         finish();
    175     }
    176 
    177     @Override
    178     public boolean onCreateOptionsMenu(Menu menu) {
    179         getMenuInflater().inflate(R.menu.captive_portal_login, menu);
    180         return true;
    181     }
    182 
    183     @Override
    184     public void onBackPressed() {
    185         WebView myWebView = (WebView) findViewById(R.id.webview);
    186         if (myWebView.canGoBack()) {
    187             myWebView.goBack();
    188         } else {
    189             super.onBackPressed();
    190         }
    191     }
    192 
    193     @Override
    194     public boolean onOptionsItemSelected(MenuItem item) {
    195         int id = item.getItemId();
    196         if (id == R.id.action_use_network) {
    197             done(true);
    198             return true;
    199         }
    200         if (id == R.id.action_do_not_use_network) {
    201             done(false);
    202             return true;
    203         }
    204         return super.onOptionsItemSelected(item);
    205     }
    206 
    207     private void testForCaptivePortal() {
    208         new Thread(new Runnable() {
    209             public void run() {
    210                 // Give time for captive portal to open.
    211                 try {
    212                     Thread.sleep(1000);
    213                 } catch (InterruptedException e) {
    214                 }
    215                 HttpURLConnection urlConnection = null;
    216                 int httpResponseCode = 500;
    217                 try {
    218                     urlConnection = (HttpURLConnection) mURL.openConnection();
    219                     urlConnection.setInstanceFollowRedirects(false);
    220                     urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
    221                     urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
    222                     urlConnection.setUseCaches(false);
    223                     urlConnection.getInputStream();
    224                     httpResponseCode = urlConnection.getResponseCode();
    225                 } catch (IOException e) {
    226                 } finally {
    227                     if (urlConnection != null) urlConnection.disconnect();
    228                 }
    229                 if (httpResponseCode == 204) {
    230                     done(true);
    231                 }
    232             }
    233         }).start();
    234     }
    235 
    236     private class MyWebViewClient extends WebViewClient {
    237         private boolean firstPageLoad = true;
    238 
    239         @Override
    240         public void onPageStarted(WebView view, String url, Bitmap favicon) {
    241             if (firstPageLoad) return;
    242             testForCaptivePortal();
    243         }
    244 
    245         @Override
    246         public void onPageFinished(WebView view, String url) {
    247             if (firstPageLoad) {
    248                 firstPageLoad = false;
    249                 // Now that WebView has loaded at least one page we know it has read in the proxy
    250                 // settings.  Now prompt the WebView read the Network-specific proxy settings.
    251                 setWebViewProxy();
    252                 // Load the real page.
    253                 view.loadUrl(mURL.toString());
    254                 return;
    255             }
    256             testForCaptivePortal();
    257         }
    258     }
    259 
    260     private class MyWebChromeClient extends WebChromeClient {
    261         @Override
    262         public void onProgressChanged(WebView view, int newProgress) {
    263             ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
    264             myProgressBar.setProgress(newProgress);
    265             myProgressBar.setVisibility(newProgress == 100 ? View.GONE : View.VISIBLE);
    266         }
    267     }
    268 }
    269