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.settings.wifi;
18 
19 import android.app.ProgressDialog;
20 import android.content.Intent;
21 import android.net.wifi.ScanResult;
22 import android.net.wifi.WifiConfiguration;
23 import android.net.wifi.WifiInfo;
24 import android.net.wifi.WifiManager;
25 import android.net.wifi.WifiManager.NetworkRequestMatchCallback;
26 import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.HandlerExecutor;
30 import android.os.Message;
31 import android.widget.Toast;
32 
33 import androidx.annotation.Nullable;
34 import androidx.annotation.VisibleForTesting;
35 import androidx.fragment.app.FragmentActivity;
36 
37 import com.android.settings.R;
38 import com.android.settings.wifi.NetworkRequestErrorDialogFragment.ERROR_DIALOG_TYPE;
39 
40 import java.util.List;
41 
42 /**
43  * When other applications request to have a wifi connection, framework will bring up this activity
44  * to let user select which wifi ap wanna to connect. This activity contains
45  * {@code NetworkRequestDialogFragment}, {@code NetworkRequestSingleSsidDialogFragment} to show UI
46  * and handles framework callback.
47  */
48 public class NetworkRequestDialogActivity extends FragmentActivity implements
49         NetworkRequestMatchCallback {
50     private static String TAG = "NetworkRequestDialogActivity";
51 
52     /** Message sent to stop scanning wifi and pop up timeout dialog. */
53     private static final int MESSAGE_STOP_SCAN_WIFI_LIST = 0;
54 
55     /** Delayed time to stop scanning wifi. */
56     private static final int DELAY_TIME_STOP_SCAN_MS = 30 * 1000;
57 
58     final static String EXTRA_IS_SPECIFIED_SSID =
59         "com.android.settings.wifi.extra.REQUEST_IS_FOR_SINGLE_NETWORK";
60 
61     @VisibleForTesting NetworkRequestDialogBaseFragment mDialogFragment;
62     private NetworkRequestUserSelectionCallback mUserSelectionCallback;
63     private boolean mIsSpecifiedSsid;
64     private boolean mShowingErrorDialog;
65     private WifiConfiguration mMatchedConfig;
66     @VisibleForTesting ProgressDialog mProgressDialog;
67 
68     @Override
onCreate(@ullable Bundle savedInstanceState)69     protected void onCreate(@Nullable Bundle savedInstanceState) {
70         super.onCreate(savedInstanceState);
71 
72         final Intent intent = getIntent();
73         if (intent != null) {
74             mIsSpecifiedSsid = intent.getBooleanExtra(EXTRA_IS_SPECIFIED_SSID, false);
75         }
76 
77         if (mIsSpecifiedSsid) {
78             showProgressDialog(getString(R.string.network_connection_searching_message));
79         } else {
80             mDialogFragment = NetworkRequestDialogFragment.newInstance();
81             mDialogFragment.show(getSupportFragmentManager(), TAG);
82         }
83     }
84 
showProgressDialog(String message)85     private void showProgressDialog(String message) {
86         dismissDialogs();
87 
88         mProgressDialog = new ProgressDialog(this);
89         mProgressDialog.setIndeterminate(true);
90         mProgressDialog.setCancelable(false);
91         mProgressDialog.setMessage(message);
92         mProgressDialog.show();
93     }
94 
showSingleSsidRequestDialog(String ssid, boolean isTryAgain)95     private void showSingleSsidRequestDialog(String ssid, boolean isTryAgain) {
96         dismissDialogs();
97 
98         mDialogFragment = new NetworkRequestSingleSsidDialogFragment();
99         final Bundle bundle = new Bundle();
100         bundle.putString(NetworkRequestSingleSsidDialogFragment.EXTRA_SSID, ssid);
101         bundle.putBoolean(NetworkRequestSingleSsidDialogFragment.EXTRA_TRYAGAIN, isTryAgain);
102         mDialogFragment.setArguments(bundle);
103         mDialogFragment.show(getSupportFragmentManager(), TAG);
104     }
105 
dismissDialogs()106     private void dismissDialogs() {
107         if (mDialogFragment != null) {
108             mDialogFragment.dismiss();
109             mDialogFragment = null;
110         }
111         if (mProgressDialog != null) {
112             mProgressDialog.dismiss();
113             mProgressDialog = null;
114         }
115     }
116 
117     @Override
onResume()118     protected void onResume() {
119         super.onResume();
120 
121         final WifiManager wifiManager = getSystemService(WifiManager.class);
122         if (wifiManager != null) {
123             wifiManager.registerNetworkRequestMatchCallback(new HandlerExecutor(mHandler), this);
124         }
125         // Sets time-out to stop scanning.
126         mHandler.sendEmptyMessageDelayed(MESSAGE_STOP_SCAN_WIFI_LIST, DELAY_TIME_STOP_SCAN_MS);
127     }
128 
129     @Override
onPause()130     protected void onPause() {
131         mHandler.removeMessages(MESSAGE_STOP_SCAN_WIFI_LIST);
132         final WifiManager wifiManager = getSystemService(WifiManager.class);
133         if (wifiManager != null) {
134             wifiManager.unregisterNetworkRequestMatchCallback(this);
135         }
136 
137         super.onPause();
138     }
139 
140     private final Handler mHandler = new Handler() {
141         @Override
142         public void handleMessage(Message msg) {
143             switch (msg.what) {
144                 case MESSAGE_STOP_SCAN_WIFI_LIST:
145                     removeMessages(MESSAGE_STOP_SCAN_WIFI_LIST);
146                     stopScanningAndPopErrorDialog(ERROR_DIALOG_TYPE.TIME_OUT);
147                     break;
148                 default:
149                     // Do nothing.
150                     break;
151             }
152         }
153     };
154 
stopScanningAndPopErrorDialog(ERROR_DIALOG_TYPE type)155     protected void stopScanningAndPopErrorDialog(ERROR_DIALOG_TYPE type) {
156         dismissDialogs();
157 
158         // Throws error dialog.
159         final NetworkRequestErrorDialogFragment dialogFragment =
160                 NetworkRequestErrorDialogFragment.newInstance();
161         dialogFragment.setRejectCallback(mUserSelectionCallback);
162         final Bundle bundle = new Bundle();
163         bundle.putSerializable(NetworkRequestErrorDialogFragment.DIALOG_TYPE, type);
164         dialogFragment.setArguments(bundle);
165         dialogFragment.show(getSupportFragmentManager(), TAG);
166         mShowingErrorDialog = true;
167     }
168 
169     @Override
onUserSelectionCallbackRegistration( NetworkRequestUserSelectionCallback userSelectionCallback)170     public void onUserSelectionCallbackRegistration(
171         NetworkRequestUserSelectionCallback userSelectionCallback) {
172         if (mIsSpecifiedSsid) {
173             mUserSelectionCallback = userSelectionCallback;
174             return;
175         }
176 
177         mDialogFragment.onUserSelectionCallbackRegistration(userSelectionCallback);
178     }
179 
180     @Override
onAbort()181     public void onAbort() {
182         stopScanningAndPopErrorDialog(ERROR_DIALOG_TYPE.ABORT);
183     }
184 
185     @Override
onMatch(List<ScanResult> scanResults)186     public void onMatch(List<ScanResult> scanResults) {
187         if (mShowingErrorDialog) {
188             // Don't do anything since error dialog shows.
189             return;
190         }
191 
192         mHandler.removeMessages(MESSAGE_STOP_SCAN_WIFI_LIST);
193 
194         if (mIsSpecifiedSsid) {
195             // Prevent from throwing same dialog, because onMatch() will be called many times.
196             if (mMatchedConfig == null) {
197                 mMatchedConfig = WifiUtils.getWifiConfig(null /* wifiEntry */, scanResults.get(0));
198                 showSingleSsidRequestDialog(
199                         WifiInfo.sanitizeSsid(mMatchedConfig.SSID), false /* isTryAgain */);
200             }
201             return;
202         }
203 
204         mDialogFragment.onMatch(scanResults);
205     }
206 
207     @Override
onUserSelectionConnectSuccess(WifiConfiguration wificonfiguration)208     public void onUserSelectionConnectSuccess(WifiConfiguration wificonfiguration) {
209         if (!isFinishing()) {
210             Toast.makeText(this, R.string.network_connection_connect_successful, Toast.LENGTH_SHORT)
211                     .show();
212             setResult(RESULT_OK);
213             finish();
214         }
215     }
216 
217     @Override
onUserSelectionConnectFailure(WifiConfiguration wificonfiguration)218     public void onUserSelectionConnectFailure(WifiConfiguration wificonfiguration) {
219         if (!isFinishing()) {
220             Toast.makeText(this, R.string.network_connection_connect_failure, Toast.LENGTH_SHORT)
221                     .show();
222             setResult(RESULT_OK);
223             finish();
224         }
225     }
226 
227     // Called when user click "Connect" button. Called by
228     // {@code NetworkRequestSingleSsidDialogFragment}.
onClickConnectButton()229     public void onClickConnectButton() {
230         if (mUserSelectionCallback != null) {
231             mUserSelectionCallback.select(mMatchedConfig);
232             showProgressDialog(getString(R.string.network_connection_connecting_message));
233         }
234     }
235 
236     // Called when user click retry button. Called by {@link NetworkRequestErrorDialogFragment}.
onClickRescanButton()237     public void onClickRescanButton() {
238         // Sets time-out to stop scanning.
239         mHandler.sendEmptyMessageDelayed(MESSAGE_STOP_SCAN_WIFI_LIST, DELAY_TIME_STOP_SCAN_MS);
240 
241         mShowingErrorDialog = false;
242 
243         if (mIsSpecifiedSsid) {
244             mMatchedConfig = null;
245             showProgressDialog(getString(R.string.network_connection_searching_message));
246         } else {
247             mDialogFragment = NetworkRequestDialogFragment.newInstance();
248             mDialogFragment.show(getSupportFragmentManager(), TAG);
249         }
250     }
251 
252     // Called when user click cancel button.
onCancel()253     public void onCancel() {
254         dismissDialogs();
255 
256         if (mUserSelectionCallback != null) {
257             mUserSelectionCallback.reject();
258         }
259         finish();
260     }
261 }
262