1 /* 2 * Copyright (C) 2018 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.hotspot2.osulogin; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; 20 21 import android.app.Activity; 22 import android.content.Context; 23 import android.graphics.Bitmap; 24 import android.net.ConnectivityManager; 25 import android.net.Network; 26 import android.net.NetworkCapabilities; 27 import android.net.NetworkRequest; 28 import android.net.wifi.WifiManager; 29 import android.os.Bundle; 30 import android.util.Log; 31 import android.view.KeyEvent; 32 import android.view.View; 33 import android.webkit.WebChromeClient; 34 import android.webkit.WebResourceError; 35 import android.webkit.WebResourceRequest; 36 import android.webkit.WebSettings; 37 import android.webkit.WebView; 38 import android.webkit.WebViewClient; 39 import android.widget.ProgressBar; 40 import android.widget.Toast; 41 42 import androidx.annotation.Nullable; 43 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 44 45 import java.net.MalformedURLException; 46 import java.net.URL; 47 48 /** 49 * Online Sign Up Login Web View launched during Provision Process of Hotspot 2.0 rel2. 50 */ 51 public class OsuLoginActivity extends Activity { 52 private static final String TAG = "OsuLogin"; 53 private static final boolean DBG = true; 54 55 private String mUrl; 56 private String mHostName; 57 private Network mNetwork; 58 private ConnectivityManager mCm; 59 private ConnectivityManager.NetworkCallback mNetworkCallback; 60 private WifiManager mWifiManager; 61 private WebView mWebView; 62 private SwipeRefreshLayout mSwipeRefreshLayout; 63 private ProgressBar mProgressBar; 64 private boolean mForceDisconnect = true; 65 boolean mRedirectResponseReceived = false; 66 67 @Override onCreate(@ullable Bundle savedInstanceState)68 protected void onCreate(@Nullable Bundle savedInstanceState) { 69 super.onCreate(savedInstanceState); 70 if (DBG) { 71 Log.d(TAG, "onCreate: Opening OSU Web View"); 72 } 73 74 mWifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); 75 if (mWifiManager == null) { 76 Log.e(TAG, "Cannot get wifi service"); 77 finishAndRemoveTask(); 78 return; 79 } 80 81 if (getIntent() == null) { 82 Log.e(TAG, "Intent is null"); 83 finishAndRemoveTask(); 84 return; 85 } 86 87 mNetwork = getIntent().getParcelableExtra(WifiManager.EXTRA_OSU_NETWORK); 88 if (mNetwork == null) { 89 Log.e(TAG, "Cannot get the network instance for OSU from intent"); 90 finishAndRemoveTask(); 91 return; 92 } 93 94 mUrl = getIntent().getStringExtra(WifiManager.EXTRA_URL); 95 if (mUrl == null) { 96 Log.e(TAG, "Cannot get OSU server url from intent"); 97 finishAndRemoveTask(); 98 return; 99 } 100 101 mHostName = getHost(mUrl); 102 if (mHostName == null) { 103 Log.e(TAG, "Cannot get host from the url"); 104 finishAndRemoveTask(); 105 return; 106 } 107 108 mCm = (ConnectivityManager) getApplicationContext().getSystemService( 109 Context.CONNECTIVITY_SERVICE); 110 if (mCm == null) { 111 Log.e(TAG, "Cannot get connectivity service"); 112 finishAndRemoveTask(); 113 return; 114 } 115 116 if (!mCm.bindProcessToNetwork(mNetwork)) { 117 Log.e(TAG, "Network is no longer valid"); 118 finishAndRemoveTask(); 119 return; 120 } 121 122 final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork); 123 if (networkCapabilities == null || !networkCapabilities.hasTransport( 124 NetworkCapabilities.TRANSPORT_WIFI)) { 125 Log.e(TAG, "WiFi is not supported for the Network"); 126 finishAndRemoveTask(); 127 return; 128 } 129 130 getActionBar().setDisplayShowHomeEnabled(false); 131 getActionBar().setElevation(0); // remove shadow 132 getActionBar().setTitle(getString(R.string.action_bar_label)); 133 getActionBar().setSubtitle(""); 134 setContentView(R.layout.osu_web_view); 135 136 // Exit this app if network disappeared. 137 mNetworkCallback = new ConnectivityManager.NetworkCallback() { 138 @Override 139 public void onLost(Network network) { 140 if (DBG) { 141 Log.d(TAG, "Lost for the current Network, close the browser"); 142 } 143 mForceDisconnect = false; // It is already disconnected. 144 if (!mRedirectResponseReceived) { 145 showSignUpFailedToast(); 146 } 147 if (mNetwork.equals(network)) { 148 finishAndRemoveTask(); 149 } 150 } 151 }; 152 153 mCm.registerNetworkCallback( 154 new NetworkRequest.Builder().addTransportType( 155 NetworkCapabilities.TRANSPORT_WIFI).removeCapability( 156 NET_CAPABILITY_TRUSTED).build(), 157 mNetworkCallback); 158 159 mWebView = findViewById(R.id.webview); 160 mWebView.clearCache(true); 161 WebSettings webSettings = mWebView.getSettings(); 162 webSettings.setJavaScriptEnabled(true); 163 webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); 164 webSettings.setUseWideViewPort(true); 165 webSettings.setLoadWithOverviewMode(true); 166 webSettings.setSupportZoom(true); 167 webSettings.setBuiltInZoomControls(true); 168 webSettings.setDisplayZoomControls(false); 169 mProgressBar = findViewById(R.id.progress_bar); 170 mWebView.setWebViewClient(new OsuWebViewClient()); 171 mWebView.setWebChromeClient(new WebChromeClient() { 172 @Override 173 public void onProgressChanged(WebView view, int newProgress) { 174 mProgressBar.setProgress(newProgress); 175 } 176 }); 177 178 if (DBG) { 179 Log.d(TAG, "OSU Web View to " + mUrl); 180 } 181 182 mWebView.loadUrl(mUrl); 183 mSwipeRefreshLayout = findViewById(R.id.swipe_refresh); 184 mSwipeRefreshLayout.setOnRefreshListener(() -> { 185 mWebView.reload(); 186 mSwipeRefreshLayout.setRefreshing(true); 187 }); 188 } 189 190 @Override onKeyDown(int keyCode, KeyEvent event)191 public boolean onKeyDown(int keyCode, KeyEvent event) { 192 // Check if the key event was the Back button. 193 if ((keyCode == KeyEvent.KEYCODE_BACK)) { 194 // If there is a history to move back 195 if (mWebView.canGoBack()) { 196 mWebView.goBack(); 197 return true; 198 } 199 } 200 return super.onKeyDown(keyCode, event); 201 } 202 203 @Override onDestroy()204 protected void onDestroy() { 205 if (mNetworkCallback != null) { 206 mCm.unregisterNetworkCallback(mNetworkCallback); 207 mNetworkCallback = null; 208 } 209 if (mWifiManager != null && mForceDisconnect) { 210 mWifiManager.disconnect(); 211 mWifiManager = null; 212 } 213 super.onDestroy(); 214 } 215 getHost(String url)216 private String getHost(String url) { 217 try { 218 return new URL(url).getHost(); 219 } catch (MalformedURLException e) { 220 Log.e(TAG, "Invalid URL " + url); 221 } 222 return null; 223 } 224 getHeaderSubtitle(String urlString)225 private String getHeaderSubtitle(String urlString) { 226 try { 227 URL url = new URL(urlString); 228 return url.getProtocol() + "://" + url.getHost(); 229 } catch (MalformedURLException e) { 230 Log.e(TAG, "Invalid URL " + urlString); 231 } 232 return ""; 233 } 234 showSignUpFailedToast()235 private void showSignUpFailedToast() { 236 Toast.makeText(getApplicationContext(), R.string.sign_up_failed, 237 Toast.LENGTH_SHORT).show(); 238 } 239 240 private class OsuWebViewClient extends WebViewClient { 241 boolean mPageError = false; 242 243 @Override onPageStarted(WebView view, String urlString, Bitmap favicon)244 public void onPageStarted(WebView view, String urlString, Bitmap favicon) { 245 String subtitle = getHeaderSubtitle(urlString); 246 getActionBar().setSubtitle(subtitle); 247 mProgressBar.setVisibility(View.VISIBLE); 248 } 249 250 @Override onPageFinished(WebView view, String url)251 public void onPageFinished(WebView view, String url) { 252 mProgressBar.setVisibility(View.INVISIBLE); 253 mSwipeRefreshLayout.setRefreshing(false); 254 255 // Do not show the page error on UI. 256 if (mPageError) { 257 if (mRedirectResponseReceived) { 258 // Do not disconnect current connection while provisioning is in progress. 259 mForceDisconnect = false; 260 } 261 finishAndRemoveTask(); 262 } 263 } 264 265 @Override onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)266 public void onReceivedError(WebView view, WebResourceRequest request, 267 WebResourceError error) { 268 if (request.getUrl().toString().startsWith("http://127.0.0.1")) { 269 mRedirectResponseReceived = true; 270 view.stopLoading(); 271 } 272 273 if (request.isForMainFrame()) { 274 // This happens right after getting HTTP redirect response from an OSU server 275 // since no more Http request is allowed to send to the OSU server. 276 mPageError = true; 277 Log.e(TAG, "onReceived Error for MainFrame: " + error.getErrorCode()); 278 } 279 } 280 } 281 } 282