Snap for 10447354 from 2e3a06d0c56edfe9b807ea64d06b05e0a7c61e50 to mainline-wifi-release

Change-Id: I1f74afb4e6b043c2ef526483b3763544231fdc72
diff --git a/Android.bp b/Android.bp
index 60ba277..55dcf84 100644
--- a/Android.bp
+++ b/Android.bp
@@ -70,6 +70,7 @@
     static_libs: [
         "ImsServiceEntitlementLib",
     ],
+    resource_dirs: [],
     optimize: {
         proguard_flags_files: ["proguard.flags"],
     },
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2311c9b..946dd8b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -26,7 +26,9 @@
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="com.google.android.setupwizard.SETUP_COMPAT_SERVICE"/>
 
-    <application>
+    <application
+      android:networkSecurityConfig="@xml/network_security_config" >
+
         <activity
             android:name=".WfcActivationActivity"
             android:exported="true"
@@ -34,6 +36,22 @@
             android:theme="@style/SudThemeGlif.Light">
         </activity>
 
+        <activity
+          android:name=".WfcQnsActivationActivity"
+          android:configChanges="orientation"
+          android:exported="true"
+          android:label="@string/emergency_address_app_label"
+          android:permission="android.permission.MODIFY_PHONE_STATE"
+          android:screenOrientation="nosensor"
+          android:theme="@android:style/Theme.Translucent.NoTitleBar"
+          android:windowFullscreen="false"
+          android:windowSoftInputMode="adjustResize">
+          <intent-filter>
+            <action android:name="android.intent.action.MAIN" />
+            <category android:name="android.intent.category.DEFAULT" />
+          </intent-filter>
+        </activity>
+
         <service
             android:name=".ImsEntitlementPollingService"
             android:exported="true"
diff --git a/OWNERS b/OWNERS
index a167e8d..6118af9 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,8 @@
+seanjstsai@google.com
 mewan@google.com
-samalin@google.com
-danielwbhuang@google.com
+
+# Backup
+akaustubh@google.com
+andymhli@google.com
+benchiu@google.com
+chihhechiang@google.com
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 981a136..71c7ec3 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -3,10 +3,5 @@
     {
       "name": "ImsServiceEntitlementUnitTests"
     }
-  ],
-  "postsubmit": [
-    {
-      "name": "ImsServiceEntitlementUnitTests"
-    }
   ]
 }
diff --git a/res/xml/network_security_config.xml b/res/xml/network_security_config.xml
new file mode 100644
index 0000000..bd37ab3
--- /dev/null
+++ b/res/xml/network_security_config.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+</network-security-config>
\ No newline at end of file
diff --git a/src/com/android/imsserviceentitlement/ActivityConstants.java b/src/com/android/imsserviceentitlement/ActivityConstants.java
index d015713..2bea9f9 100644
--- a/src/com/android/imsserviceentitlement/ActivityConstants.java
+++ b/src/com/android/imsserviceentitlement/ActivityConstants.java
@@ -35,6 +35,9 @@
     public static final int LAUNCH_APP_UPDATE = 1;
     public static final int LAUNCH_APP_SHOW_TC = 2;
 
+    /** Constants shared by QNS module */
+    public static final String EXTRA_URL = "EXTRA_URL";
+
     /**
      * Returns {@code true} if the app is launched for WFC activation; {@code false} for emergency
      * address update or displaying terms & conditions.
@@ -67,5 +70,13 @@
         return subId;
     }
 
+    public static String getUrl(Intent intent) {
+        String url = "";
+        if (intent == null || (url = intent.getStringExtra(EXTRA_URL)) == null) {
+            return "";
+        }
+        return url;
+    }
+
     private ActivityConstants() {}
 }
diff --git a/src/com/android/imsserviceentitlement/ImsEntitlementApi.java b/src/com/android/imsserviceentitlement/ImsEntitlementApi.java
index 74943ac..d6ccd65 100644
--- a/src/com/android/imsserviceentitlement/ImsEntitlementApi.java
+++ b/src/com/android/imsserviceentitlement/ImsEntitlementApi.java
@@ -26,6 +26,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.imsserviceentitlement.debug.DebugUtils;
 import com.android.imsserviceentitlement.entitlement.EntitlementConfiguration;
 import com.android.imsserviceentitlement.entitlement.EntitlementConfiguration.ClientBehavior;
 import com.android.imsserviceentitlement.entitlement.EntitlementResult;
@@ -75,7 +76,13 @@
         this.mSubId = subId;
         CarrierConfig carrierConfig = getCarrierConfig(context);
         this.mNeedsImsProvisioning = TelephonyUtils.isImsProvisioningRequired(context, subId);
-        this.mServiceEntitlement = new ServiceEntitlement(context, carrierConfig, subId);
+        this.mServiceEntitlement =
+                new ServiceEntitlement(
+                        context,
+                        carrierConfig,
+                        subId,
+                        /* saveHttpHistory = */ false,
+                        DebugUtils.getBypassEapAkaResponse());
         this.mLastEntitlementConfiguration = new EntitlementConfiguration(context, subId);
     }
 
diff --git a/src/com/android/imsserviceentitlement/WfcWebPortalFragment.java b/src/com/android/imsserviceentitlement/WfcWebPortalFragment.java
index 7249fc3..1619324 100644
--- a/src/com/android/imsserviceentitlement/WfcWebPortalFragment.java
+++ b/src/com/android/imsserviceentitlement/WfcWebPortalFragment.java
@@ -95,6 +95,9 @@
                 new OnAttachStateChangeListener() {
                     @Override
                     public void onViewAttachedToWindow(View v) {
+                        if (!v.hasFocus()) {
+                            v.requestFocus();
+                        }
                     }
 
                     @Override
diff --git a/src/com/android/imsserviceentitlement/debug/DebugUtils.java b/src/com/android/imsserviceentitlement/debug/DebugUtils.java
index 8936948..ddd71d6 100644
--- a/src/com/android/imsserviceentitlement/debug/DebugUtils.java
+++ b/src/com/android/imsserviceentitlement/debug/DebugUtils.java
@@ -25,6 +25,7 @@
 /** Provides API for debugging and not allow to debug on user build. */
 public final class DebugUtils {
     private static final String PROP_PII_LOGGABLE = "dbg.imsse.pii_loggable";
+    private static final String PROP_BYPASS_EAP_AKA_RESPONSE = "dbg.imsse.bypass_eap_aka_response";
     private static final String PROP_SERVER_URL_OVERRIDE = "persist.dbg.imsse.server_url";
     private static final String BUILD_TYPE_USER = "user";
 
@@ -48,6 +49,15 @@
         return SystemProperties.getBoolean(PROP_PII_LOGGABLE, false);
     }
 
+    /** Returns a non empty string if bypass EAP-AKA authentication is enabled. */
+    public static String getBypassEapAkaResponse() {
+        if (!isDebugBuild()) {
+            return "";
+        }
+
+        return SystemProperties.get(PROP_BYPASS_EAP_AKA_RESPONSE);
+    }
+
     /**
      * Returns {@link Optional} if testing server url was set in system property.
      */
diff --git a/src/com/android/imsserviceentitlement/qns/WfcQnsActivationActivity.java b/src/com/android/imsserviceentitlement/qns/WfcQnsActivationActivity.java
new file mode 100644
index 0000000..997eac7
--- /dev/null
+++ b/src/com/android/imsserviceentitlement/qns/WfcQnsActivationActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 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.imsserviceentitlement;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.android.imsserviceentitlement.ActivityConstants;
+import com.android.imsserviceentitlement.R;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentTransaction;
+
+/** Main activity to handle VoWiFi activation on QNS module*/
+public class WfcQnsActivationActivity extends FragmentActivity {
+
+  public static final String TAG = "IMSSE-WfcQnsActivationActivity";
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.activity_wfc_activation);
+    new Handler(getMainLooper()).post(this::startWebPortalFragment);
+  }
+
+  private void startWebPortalFragment() {
+    String url = ActivityConstants.getUrl(getIntent());
+    if (url.isEmpty()) {
+      return;
+    }
+    WfcQnsWebPortalFragment wfcQnsWebPortalFragment = WfcQnsWebPortalFragment.newInstance(url);
+    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+    ft.replace(R.id.wfc_activation_container, wfcQnsWebPortalFragment);
+    ft.commit();
+  }
+}
diff --git a/src/com/android/imsserviceentitlement/qns/WfcQnsWebPortalFragment.java b/src/com/android/imsserviceentitlement/qns/WfcQnsWebPortalFragment.java
new file mode 100644
index 0000000..58e7228
--- /dev/null
+++ b/src/com/android/imsserviceentitlement/qns/WfcQnsWebPortalFragment.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2021 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.imsserviceentitlement;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ProgressBar;
+
+import com.android.imsserviceentitlement.R;
+
+import androidx.fragment.app.Fragment;
+
+/** A fragment of WebView to render emergency address web portal */
+public class WfcQnsWebPortalFragment extends Fragment {
+  private static final String TAG = "IMSSE-WfcQnsWebPortalFragment";
+
+  private static final String URL_KEY = "URL_KEY";
+
+  /** Public static constructor */
+  public static WfcQnsWebPortalFragment newInstance(String url) {
+    WfcQnsWebPortalFragment frag = new WfcQnsWebPortalFragment();
+
+    Bundle args = new Bundle();
+    args.putString(URL_KEY, url);
+    frag.setArguments(args);
+
+    return frag;
+  }
+
+  @SuppressLint("SetJavaScriptEnabled") // only trusted URLs are loaded
+  @Override
+  public View onCreateView(
+      LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+    View v = inflater.inflate(R.layout.fragment_webview, container, false);
+
+    Bundle arguments = getArguments();
+    String url = arguments.getString(URL_KEY);
+
+    ProgressBar spinner = (ProgressBar) v.findViewById(R.id.loadingbar);
+    WebView webView = (WebView) v.findViewById(R.id.webview);
+    webView.setWebViewClient(
+        new WebViewClient() {
+          private boolean hideLoader = false;
+
+          @Override
+          public boolean shouldOverrideUrlLoading(WebView view, String url) {
+            Log.d(TAG, "shouldOverrideUrlLoading()");
+            return false; // Let WebView handle redirected URL
+          }
+
+          @Override
+          public void onPageStarted(WebView webview, String url, Bitmap favicon) {
+            // Webview will be invisible for first time only
+            Log.d(TAG, "onPageStarted()");
+            if (!hideLoader) {
+              webview.setVisibility(WebView.INVISIBLE);
+              Log.d(TAG, "onPageStarted() setVisibility(WebView.INVISIBLE)");
+            }
+          }
+
+          @Override
+          public void onPageFinished(WebView view, String url) {
+            Log.d(TAG, "onPageFinished()");
+            hideLoader = true;
+            spinner.setVisibility(View.GONE);
+            view.setVisibility(WebView.VISIBLE);
+            super.onPageFinished(view, url);
+          }
+        });
+
+    webView.addJavascriptInterface(new JsInterface(), JsInterface.OBJECT_NAME);
+
+    WebSettings settings = webView.getSettings();
+    settings.setDomStorageEnabled(true);
+    settings.setJavaScriptEnabled(true);
+
+    webView.loadUrl(url);
+
+    return v;
+  }
+
+  // JS interface required by Rogers web portal.
+  private class JsInterface {
+    /**
+     * Name of the JS controller object.
+     *
+     * <p>It's hard-coded in the JS; DO NOT change unless requested by Rogers.
+     */
+    public static final String OBJECT_NAME = "WiFiCallingWebViewController";
+
+    /**
+     * Finish the activity when the cancel button clicked.
+     *
+     * <p>The method name is hard-coded in the JS; DO NOT change unless requested by Rogers.
+     */
+    @JavascriptInterface
+    public void cancelButtonClicked() {
+      Log.d(TAG, "cancelButtonClicked()");
+      final Activity activity = WfcQnsWebPortalFragment.this.getActivity();
+      if (activity != null) {
+        activity.setResult(Activity.RESULT_CANCELED);
+        activity.finish();
+      }
+    }
+
+    /**
+     * This method is invoked in JS but no implementation required. So define it to avoid JS
+     * failure.
+     *
+     * <p>The method name is hard-coded in the JS; DO NOT change unless requested by Rogers.
+     */
+    @JavascriptInterface
+    public void cancelButtonPressed() {
+      Log.d(TAG, "cancelButtonPressed()");
+    }
+
+    /**
+     * This method is invoked in JS but no implementation required. So define it to avoid JS
+     * failure.
+     *
+     * <p>The method name is hard-coded in the JS; DO NOT change unless requested by Rogers.
+     */
+    @JavascriptInterface
+    public void phoneServicesAccountStatusChanged() {
+      Log.d(TAG, "phoneServicesAccountStatusChanged()");
+      // No-op
+    }
+
+    /**
+     * Finish the activity when onCloseWebView() is called.
+     *
+     * <p>The method name is hard-coded in the JS; DO NOT change unless requested by Bell.
+     */
+    @JavascriptInterface
+    @SuppressWarnings("checkstyle:MethodName")
+    public void CloseWebView() {
+      Log.d(TAG, "CloseWebView()");
+      final Activity activity = WfcQnsWebPortalFragment.this.getActivity();
+      if (activity != null) {
+        activity.setResult(Activity.RESULT_CANCELED);
+        activity.finish();
+      }
+    }
+  }
+}