• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 package com.android.telephony.qns.wfc;
17 
18 import android.app.Activity;
19 import android.app.AlertDialog;
20 import android.app.Dialog;
21 import android.app.ProgressDialog;
22 import android.annotation.Nullable;
23 import android.content.ComponentName;
24 import android.content.DialogInterface.OnClickListener;
25 import android.content.Intent;
26 import android.net.Uri;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.provider.Settings;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.view.ContextThemeWrapper;
34 import android.view.WindowManager;
35 
36 import androidx.activity.result.ActivityResultLauncher;
37 import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
38 import androidx.browser.customtabs.CustomTabsClient;
39 import androidx.browser.customtabs.CustomTabsIntent;
40 import androidx.browser.customtabs.CustomTabsServiceConnection;
41 import androidx.browser.customtabs.CustomTabsSession;
42 import androidx.fragment.app.DialogFragment;
43 import androidx.fragment.app.FragmentActivity;
44 
45 import com.android.internal.annotations.VisibleForTesting;
46 import com.android.telephony.qns.R;
47 
48 /** Main activity to handle VoWiFi activation */
49 public class WfcActivationActivity extends FragmentActivity {
50 
51     public static final String TAG = "QNS-WfcActivationActivity";
52 
53     private static final String EXTRA_URL = "EXTRA_URL";
54 
55     // Message IDs
56     private static final int MESSAGE_CHECK_WIFI = 1;
57     private static final int MESSAGE_CHECK_WIFI_DONE = 2;
58     private static final int MESSAGE_TRY_EPDG_CONNECTION = 3;
59     private static final int MESSAGE_TRY_EPDG_CONNECTION_DONE = 4;
60     private static final int MESSAGE_SHOW_WEB_PORTAL = 5;
61 
62     private WfcActivationHelper mWfcActivationHelper;
63 
64     private Handler mUiHandler;
65     @VisibleForTesting ProgressDialog mProgressDialog;
66 
67     private CustomTabsSession mCustomTabsSession;
68     @VisibleForTesting CustomTabsServiceConnection mServiceConnection;
69     private ActivityResultLauncher<Intent> mWebviewResultsLauncher =
70         registerForActivityResult(
71             new StartActivityForResult(),
72             activityResult -> {
73               if (activityResult.getResultCode() == Activity.RESULT_CANCELED) {
74                 Log.d(TAG, "Webview Activity Result CANCEL");
75                 finish();
76               } else {
77                 Log.d(TAG, "Webview Activity Result OK");
78                 finish();
79               }
80             });
81 
82     // Whether it's safe now to update UI, based on activity visibility.
83     // It should be true between onResume() and onPause().
84     private boolean mSafeToUpdateUi = false;
85 
86     @Override
onCreate(Bundle savedInstanceState)87     protected void onCreate(Bundle savedInstanceState) {
88         // Initialization
89         super.onCreate(savedInstanceState);
90         createDependencies();
91         createUiHandler();
92 
93         // Set layout
94         setContentView(R.layout.activity_wfc_activation);
95 
96         if (WfcUtils.isActivationFlow(getIntent())) {
97             // WFC activation flow
98             mUiHandler.sendEmptyMessage(MESSAGE_CHECK_WIFI);
99         } else {
100             // Emergency address update flow
101             mUiHandler.sendEmptyMessage(MESSAGE_SHOW_WEB_PORTAL);
102         }
103     }
104 
105     @Override
onResume()106     protected void onResume() {
107         super.onResume();
108         mSafeToUpdateUi = true;
109     }
110 
111     @Override
onPause()112     protected void onPause() {
113         super.onPause();
114         mSafeToUpdateUi = false;
115     }
116 
createUiHandler()117     private void createUiHandler() {
118         Handler.Callback handlerCallback =
119                 (Message msg) -> {
120                     Log.d(TAG, "UiHandler received: " + msg);
121                     switch (msg.what) {
122                         case MESSAGE_CHECK_WIFI:
123                             mWfcActivationHelper.checkWiFi(
124                                     mUiHandler.obtainMessage(MESSAGE_CHECK_WIFI_DONE));
125                             break;
126                         case MESSAGE_CHECK_WIFI_DONE:
127                             if (msg.arg1 == WfcActivationHelper.WIFI_CONNECTION_SUCCESS) {
128                                 mUiHandler.sendEmptyMessage(MESSAGE_TRY_EPDG_CONNECTION);
129                             } else { // msg.arg1 == WfcActivationHelper.WIFI_CONNECTION_ERROR
130                                 showWiFiUnavailableDialog();
131                             }
132                             break;
133                         case MESSAGE_TRY_EPDG_CONNECTION:
134                             showProgressDialog();
135                             Log.d(TAG, "Show progress dialog - tryEpdgConnectionOverWiFi");
136                             mWfcActivationHelper.tryEpdgConnectionOverWiFi(
137                                     mUiHandler.obtainMessage(MESSAGE_TRY_EPDG_CONNECTION_DONE),
138                                     mWfcActivationHelper
139                                             .getVowifiRegistrationTimerForVowifiActivation());
140                             break;
141                         case MESSAGE_TRY_EPDG_CONNECTION_DONE:
142                             dismissProgressDialog();
143                             Log.d(TAG, "Dismiss progress dialog - tryEpdgConnectionOverWiFi");
144                             if (msg.arg1 == WfcActivationHelper.EPDG_CONNECTION_SUCCESS) {
145                                 Log.d(TAG, "VoWiFi activated");
146                                 setResultAndFinish(RESULT_OK);
147                             } else { // msg.arg1 == WfcActivationHelper.EPDG_CONNECTION_ERROR
148                                 mUiHandler.sendEmptyMessage(MESSAGE_SHOW_WEB_PORTAL);
149                             }
150                             break;
151                         case MESSAGE_SHOW_WEB_PORTAL:
152                             startWebPortal();
153                             break;
154                         default:
155                             Log.e(TAG, "UiHandler received unknown message: " + msg);
156                             return false;
157                     }
158                     return true;
159                 };
160         mUiHandler = new Handler(handlerCallback);
161     }
162 
163     @Override
onDestroy()164     public void onDestroy() {
165         if (mServiceConnection != null) {
166           unbindService(mServiceConnection);
167         }
168         super.onDestroy();
169     }
170 
startWebPortal()171     private void startWebPortal() {
172       Log.d(TAG, "starting web portal ..");
173       if (!mSafeToUpdateUi) {
174         Log.d(TAG, "Not safe to update UI. Stopping.");
175         return;
176       }
177       String url = mWfcActivationHelper.getWebPortalUrl();
178       if (TextUtils.isEmpty(url)) {
179         Log.d(TAG, "No web portal url!");
180         return;
181       }
182       if (!mWfcActivationHelper.supportJsCallbackForVowifiPortal()) {
183         // For carriers not requiring JS callback in their WFC activation webpage, using a
184         // ChromeCustomTab provides richer web functionality while avoiding jumping to the browser
185         // app and introducing a discontinuity in UX.
186         startCustomTab(url);
187       } else {
188         // Because QNS uses system UID now, webview cannot be started here. Instead, webview is
189         // started in a different activity, {@code R.string.webview_component}.
190         startWebPortalActivity();
191       }
192     }
193 
startCustomTab(String url)194     private void startCustomTab(String url) {
195         mServiceConnection =
196             new CustomTabsServiceConnection() {
197               @Override
198               public void onCustomTabsServiceConnected(
199                       ComponentName name, CustomTabsClient client) {
200                 client.warmup(0L);
201                 mCustomTabsSession = client.newSession(null);
202                 mCustomTabsSession.mayLaunchUrl(Uri.parse(url), null, null);
203               }
204 
205               @Override
206               public void onServiceDisconnected(ComponentName name) {
207                 mCustomTabsSession = null;
208               }
209             };
210 
211         String ServicePackageName =
212                 getResources().getString(R.string.custom_tabs_service_package_name);
213         CustomTabsClient.bindCustomTabsService(this, ServicePackageName, mServiceConnection);
214         new CustomTabsIntent.Builder(mCustomTabsSession).build().launchUrl(this, Uri.parse(url));
215 
216         if (WfcUtils.isActivationFlow(getIntent())) {
217           setResultAndFinish(RESULT_CANCELED);
218         } else {
219           setResultAndFinish(RESULT_OK);
220         }
221     }
startWebPortalActivity()222     private void startWebPortalActivity() {
223         String webviewComponent = getResources().getString(R.string.webview_component);
224         ComponentName componentName = ComponentName.unflattenFromString(webviewComponent);
225         String url = mWfcActivationHelper.getWebPortalUrl();
226 
227         Log.d(TAG, "startWebPortalActivity componentName: " + componentName);
228         Intent intent = new Intent();
229         intent.setComponent(componentName);
230         intent.putExtra(EXTRA_URL, url);
231         mWebviewResultsLauncher.launch(intent);
232     }
233 
showProgressDialog()234     private void showProgressDialog() {
235         if (!mSafeToUpdateUi) {
236             return;
237         }
238         if (mProgressDialog != null && mProgressDialog.isShowing()) {
239             return;
240         }
241         mProgressDialog =
242                 new ProgressDialog(
243                         new ContextThemeWrapper(
244                                 this, android.R.style.Theme_DeviceDefault_Light_Dialog));
245         mProgressDialog.setCancelable(false);
246         mProgressDialog.setCanceledOnTouchOutside(false);
247         mProgressDialog.setMessage(getText(R.string.progress_text));
248         mProgressDialog.show();
249         // Keep screen on
250         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
251     }
252 
dismissProgressDialog()253     private void dismissProgressDialog() {
254         if (!mSafeToUpdateUi) {
255             return;
256         }
257         if (mProgressDialog != null) {
258             mProgressDialog.dismiss();
259             mProgressDialog = null;
260             // Allow screen off
261             getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
262         }
263     }
264 
showWiFiUnavailableDialog()265     private void showWiFiUnavailableDialog() {
266         if (!mSafeToUpdateUi) {
267             return;
268         }
269         DialogFragment dialog =
270                 AlertDialogFragment.newInstance(
271                         R.string.connect_to_wifi_or_web_portal_title,
272                         R.string.connect_to_wifi_or_web_portal_message);
273         dialog.show(getSupportFragmentManager(), "Wifi_unavailable_dialog");
274     }
275 
276     /** Dialog fragment to show error messages */
277     public static class AlertDialogFragment extends DialogFragment {
278 
279         private static final String TITLE_KEY = "TITLE_KEY";
280         private static final String MESSAGE_KEY = "MESSAGE_KEY";
281 
282         /** Static constructor */
newInstance(int titleId, int messageId)283         public static AlertDialogFragment newInstance(int titleId, int messageId) {
284             AlertDialogFragment frag = new AlertDialogFragment();
285             frag.setCancelable(false);
286 
287             Bundle args = new Bundle();
288             args.putInt(TITLE_KEY, titleId);
289             args.putInt(MESSAGE_KEY, messageId);
290             frag.setArguments(args);
291 
292             return frag;
293         }
294 
295         @Override
onCreateDialog(Bundle savedInstanceState)296         public Dialog onCreateDialog(Bundle savedInstanceState) {
297             Bundle args = getArguments();
298             int titleId = args.getInt(TITLE_KEY);
299             int messageId = args.getInt(MESSAGE_KEY);
300             final WfcActivationActivity activity =
301                     (WfcActivationActivity) getActivity();
302             return new AlertDialog.Builder(
303                             new ContextThemeWrapper(
304                                     getActivity(),
305                                     android.R.style.Theme_DeviceDefault_Light_Dialog))
306                     .setTitle(titleId)
307                     .setMessage(messageId)
308                     .setPositiveButton(
309                             R.string.button_setup_web_portal,
310                             (OnClickListener)
311                                     (dialog, which) ->
312                                             activity.mUiHandler.sendEmptyMessage(
313                                                     MESSAGE_SHOW_WEB_PORTAL))
314                     .setNegativeButton(
315                             R.string.button_turn_on_wifi,
316                             (OnClickListener)
317                                     (dialog, which) -> {
318                                         // Redirect to WiFi settings UI
319                                         Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
320                                         activity.startActivity(intent);
321                                         // And finish self
322                                         activity.setResultAndFinish(RESULT_CANCELED);
323                                     })
324                     .create();
325         }
326     }
327 
setResultAndFinish(int resultCode)328     private void setResultAndFinish(int resultCode) {
329         setResult(resultCode);
330         finish();
331     }
332 
createDependencies()333     private void createDependencies() {
334         // Default initialization for production
335         int subId = WfcUtils.getSubId(getIntent());
336 
337         if (WfcUtils.getWfcActivationHelper() != null) {
338             mWfcActivationHelper = WfcUtils.getWfcActivationHelper();
339           Log.v(TAG, "WfcActivationHelper injected: " + mWfcActivationHelper);
340         } else {
341             mWfcActivationHelper = new WfcActivationHelper(this, subId);
342         }
343 
344         if (WfcUtils.getWebviewResultLauncher() != null) {
345             mWebviewResultsLauncher = WfcUtils.getWebviewResultLauncher();
346             Log.v(TAG, "getWebviewResultLauncher injected: " + mWebviewResultsLauncher);
347         }
348     }
349 }
350