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