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