Passpoint-r2: OsuLogin Application
This application is launched with the OSU url provided during Provisioning flow
so that user provides the information for subscription.
Bug: 112052266
Test: live tests with passpoint r2 AP
Change-Id: I8f2218876756a069ee3d643f60c9bcda9b853c97
Signed-off-by: Ecco Park <eccopark@google.com>
diff --git a/OsuLogin/Android.mk b/OsuLogin/Android.mk
new file mode 100644
index 0000000..f04227b
--- /dev/null
+++ b/OsuLogin/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_USE_AAPT2 := true
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := OsuLogin
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/OsuLogin/AndroidManifest.xml b/OsuLogin/AndroidManifest.xml
new file mode 100644
index 0000000..f6cd79b
--- /dev/null
+++ b/OsuLogin/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.hotspot2">
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:enabled="true"
+ android:label="@string/app_name"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:supportsRtl="true">
+ <activity android:name="com.android.hotspot2.osu.OsuLoginActivity">
+ <intent-filter>
+ <action android:name="android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/OsuLogin/OWNERS b/OsuLogin/OWNERS
new file mode 100644
index 0000000..aa7c3e6
--- /dev/null
+++ b/OsuLogin/OWNERS
@@ -0,0 +1,2 @@
+satk@google.com
+etancohen@google.com
diff --git a/OsuLogin/res/layout/osu_web_view.xml b/OsuLogin/res/layout/osu_web_view.xml
new file mode 100644
index 0000000..4eafb39
--- /dev/null
+++ b/OsuLogin/res/layout/osu_web_view.xml
@@ -0,0 +1,13 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <WebView
+ android:id="@+id/webview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true" />
+
+</FrameLayout>
diff --git a/OsuLogin/res/values/dimens.xml b/OsuLogin/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/OsuLogin/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/OsuLogin/res/values/strings.xml b/OsuLogin/res/values/strings.xml
new file mode 100644
index 0000000..9fafeef
--- /dev/null
+++ b/OsuLogin/res/values/strings.xml
@@ -0,0 +1,4 @@
+<resources>
+ <!-- application name [CHAR LIMIT=32] -->
+ <string name="app_name">OsuLogin</string>
+</resources>
diff --git a/OsuLogin/res/values/styles.xml b/OsuLogin/res/values/styles.xml
new file mode 100644
index 0000000..f6c2339
--- /dev/null
+++ b/OsuLogin/res/values/styles.xml
@@ -0,0 +1,19 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="@android:style/Theme.DeviceDefault.Settings">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+</resources>
diff --git a/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java b/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
new file mode 100644
index 0000000..6a48904
--- /dev/null
+++ b/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hotspot2.osu;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.webkit.WebResourceError;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import com.android.hotspot2.R;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Online Sign Up Login Web View launched during Provision Process of Hotspot 2.0 rel2.
+ */
+public class OsuLoginActivity extends Activity {
+ private static final String TAG = "OsuLogin";
+ private static final boolean DBG = true;
+
+ private String mUrl;
+ private String mHostName;
+ private Network mNetwork;
+ private ConnectivityManager mCm;
+ private ConnectivityManager.NetworkCallback mNetworkCallback;
+ private WifiManager mWifiManager;
+ private WebView mWebView;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (DBG) {
+ Log.d(TAG, "onCreate: Opening OSU Web View");
+ }
+
+ mWifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ if (mWifiManager == null) {
+ Log.e(TAG, "Cannot get wifi service");
+ finishAndRemoveTask();
+ return;
+ }
+
+ if (getIntent() == null) {
+ Log.e(TAG, "Intent is null");
+ finishAndRemoveTask();
+ return;
+ }
+
+ mNetwork = getIntent().getParcelableExtra(WifiManager.EXTRA_OSU_NETWORK);
+ if (mNetwork == null) {
+ Log.e(TAG, "Cannot get the network instance for OSU from intent");
+ finishAndRemoveTask();
+ return;
+ }
+
+ mUrl = getIntent().getStringExtra(WifiManager.EXTRA_URL);
+ if (mUrl == null) {
+ Log.e(TAG, "Cannot get OSU server url from intent");
+ finishAndRemoveTask();
+ return;
+ }
+
+ mHostName = getHost(mUrl);
+ if (mHostName == null) {
+ Log.e(TAG, "Cannot get host from the url");
+ finishAndRemoveTask();
+ return;
+ }
+
+ mCm = (ConnectivityManager) getApplicationContext().getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ if (mCm == null) {
+ Log.e(TAG, "Cannot get connectivity service");
+ finishAndRemoveTask();
+ return;
+ }
+
+ if (!mCm.bindProcessToNetwork(mNetwork)) {
+ Log.e(TAG, "Network is no longer valid");
+ finishAndRemoveTask();
+ return;
+ }
+
+ final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
+ if (networkCapabilities == null || !networkCapabilities.hasTransport(
+ NetworkCapabilities.TRANSPORT_WIFI)) {
+ Log.e(TAG, "WiFi is not supported for the Network");
+ finishAndRemoveTask();
+ return;
+ }
+
+ getActionBar().setDisplayShowHomeEnabled(false);
+ setContentView(R.layout.osu_web_view);
+
+ // Exit this app if network disappeared.
+ mNetworkCallback = new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onLost(Network network) {
+ if (DBG) {
+ Log.d(TAG, "Lost for the current Network, close the browser");
+ }
+ if (mNetwork.equals(network)) {
+ finishAndRemoveTask();
+ }
+ }
+ };
+
+ mCm.registerNetworkCallback(
+ new NetworkRequest.Builder().addTransportType(
+ NetworkCapabilities.TRANSPORT_WIFI).build(),
+ mNetworkCallback);
+
+ mWebView = findViewById(R.id.webview);
+ mWebView.clearCache(true);
+ WebSettings webSettings = mWebView.getSettings();
+ webSettings.setJavaScriptEnabled(true);
+ webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
+ webSettings.setUseWideViewPort(true);
+ webSettings.setLoadWithOverviewMode(true);
+ webSettings.setSupportZoom(true);
+ webSettings.setBuiltInZoomControls(true);
+ webSettings.setDisplayZoomControls(false);
+
+ mWebView.setWebViewClient(new OsuWebViewClient());
+ if (DBG) {
+ Log.d(TAG, "OSU Web View to " + mUrl);
+ }
+ mWebView.loadUrl(mUrl);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // Check if the key event was the Back button.
+ if ((keyCode == KeyEvent.KEYCODE_BACK)) {
+ // If there is a history to move back
+ if (mWebView.canGoBack()){
+ mWebView.goBack();
+ return true;
+ }
+
+ // In case of back key, it needs to disconnect current connection with OSU AP to
+ // abort current Provisioning flow.
+ if (mWifiManager != null) {
+ mWifiManager.disconnect();
+ }
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mNetworkCallback != null) {
+ mCm.unregisterNetworkCallback(mNetworkCallback);
+ }
+ super.onDestroy();
+ }
+
+ private String getHost(String url) {
+ try {
+ return new URL(url).getHost();
+ } catch (MalformedURLException e) {
+ Log.e(TAG, "Invalid URL " + url);
+ }
+ return null;
+ }
+
+ private class OsuWebViewClient extends WebViewClient {
+ boolean mPageError = false;
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ // Do not show the page error on UI.
+ if (mPageError) {
+ finishAndRemoveTask();
+ }
+ }
+
+ @Override
+ public void onReceivedError(WebView view, WebResourceRequest request,
+ WebResourceError error) {
+ if (request.isForMainFrame()) {
+ // This happens right after getting HTTP redirect response from an OSU server
+ // since no more Http request is allowed to send to the OSU server.
+ mPageError = true;
+ Log.e(TAG, "onReceived Error for MainFrame: " + error.getErrorCode());
+ }
+ }
+ }
+}