• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.tv.settings.connectivity.setup;
18 
19 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED;
20 
21 import android.annotation.Nullable;
22 import android.content.Context;
23 import android.net.ConnectivityManager;
24 import android.net.Network;
25 import android.net.NetworkCapabilities;
26 import android.net.NetworkInfo;
27 import android.net.wifi.WifiConfiguration;
28 import android.net.wifi.WifiInfo;
29 import android.net.wifi.WifiManager;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.os.Message;
33 import android.util.Log;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.widget.TextView;
38 
39 import androidx.annotation.VisibleForTesting;
40 import androidx.fragment.app.Fragment;
41 import androidx.fragment.app.FragmentActivity;
42 import androidx.lifecycle.ViewModelProviders;
43 
44 import com.android.settingslib.wifi.AccessPoint;
45 import com.android.tv.settings.R;
46 import com.android.tv.settings.connectivity.ConnectivityListener;
47 import com.android.tv.settings.connectivity.security.WifiSecurityHelper;
48 import com.android.tv.settings.connectivity.util.State;
49 import com.android.tv.settings.connectivity.util.StateMachine;
50 
51 import java.lang.ref.WeakReference;
52 import java.util.List;
53 
54 /**
55  * State responsible for showing the connect page.
56  */
57 public class ConnectState implements State {
58     private final FragmentActivity mActivity;
59     private Fragment mFragment;
60 
ConnectState(FragmentActivity wifiSetupActivity)61     public ConnectState(FragmentActivity wifiSetupActivity) {
62         this.mActivity = wifiSetupActivity;
63     }
64 
65     @Override
processForward()66     public void processForward() {
67         FragmentChangeListener listener = (FragmentChangeListener) mActivity;
68         mFragment = ConnectToWifiFragment.newInstance("", true);
69         if (listener != null) {
70             listener.onFragmentChange(mFragment, true);
71         }
72     }
73 
74     @Override
processBackward()75     public void processBackward() {
76         StateMachine stateMachine = ViewModelProviders.of(mActivity).get(StateMachine.class);
77         stateMachine.back();
78     }
79 
80     @Override
getFragment()81     public Fragment getFragment() {
82         return mFragment;
83     }
84 
85     /**
86      * Connects to the wifi network specified by the given configuration.
87      */
88     public static class ConnectToWifiFragment extends MessageFragment
89             implements ConnectivityListener.WifiNetworkListener {
90 
91         @VisibleForTesting
92         static final int MSG_TIMEOUT = 1;
93         @VisibleForTesting
94         static final int CONNECTION_TIMEOUT = 60000;
95         private static final String TAG = "ConnectToWifiFragment";
96         private static final boolean DEBUG = false;
97         @VisibleForTesting
98         StateMachine mStateMachine;
99         @VisibleForTesting
100         WifiConfiguration mWifiConfiguration;
101         @VisibleForTesting
102         WifiManager mWifiManager;
103         @VisibleForTesting
104         Handler mHandler;
105         private ConnectivityListener mConnectivityListener;
106         private ConnectivityManager mConnectivityManager;
107         private UserChoiceInfo mUserChoiceInfo;
108 
109         /**
110          * Obtain a new instance of ConnectToWifiFragment.
111          *
112          * @param title                 title of fragment.
113          * @param showProgressIndicator whether show progress indicator.
114          * @return new instance.
115          */
newInstance(String title, boolean showProgressIndicator)116         public static ConnectToWifiFragment newInstance(String title,
117                 boolean showProgressIndicator) {
118             ConnectToWifiFragment fragment = new ConnectToWifiFragment();
119             Bundle args = new Bundle();
120             addArguments(args, title, showProgressIndicator);
121             fragment.setArguments(args);
122             return fragment;
123         }
124 
125         @Override
onCreate(Bundle icicle)126         public void onCreate(Bundle icicle) {
127             super.onCreate(icicle);
128             mUserChoiceInfo = ViewModelProviders.of(getActivity()).get(UserChoiceInfo.class);
129             mConnectivityListener = new ConnectivityListener(getActivity(), null);
130             mConnectivityListener.start();
131             mConnectivityManager = (ConnectivityManager) getActivity().getSystemService(
132                     Context.CONNECTIVITY_SERVICE);
133 
134             mUserChoiceInfo = ViewModelProviders.of(getActivity()).get(UserChoiceInfo.class);
135             mWifiConfiguration = WifiSecurityHelper.getConfig(getActivity());
136 
137             mStateMachine = ViewModelProviders
138                     .of(getActivity()).get(StateMachine.class);
139 
140             mWifiManager = ((WifiManager) getActivity().getApplicationContext()
141                     .getSystemService(Context.WIFI_SERVICE));
142             mHandler = new MessageHandler(this);
143             mConnectivityListener.setWifiListener(this);
144         }
145 
146         @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle)147         public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle) {
148             View view = super.onCreateView(inflater, container, icicle);
149             ((TextView) view.findViewById(R.id.status_text)).setText(
150                     getContext().getString(R.string.wifi_connecting,
151                             WifiSecurityHelper.getSsid(getActivity())));
152             return view;
153         }
154 
155         @Override
onResume()156         public void onResume() {
157             super.onResume();
158             postTimeout();
159             proceedDependOnNetworkState();
160         }
161 
162         @VisibleForTesting
proceedDependOnNetworkState()163         void proceedDependOnNetworkState() {
164             if (isNetworkConnected()) {
165                 mWifiManager.disconnect();
166             }
167 
168             int easyConnectNetworkId = mUserChoiceInfo.getEasyConnectNetworkId();
169             if (easyConnectNetworkId != -1) {
170                 if (DEBUG) Log.d(TAG, "Starting to connect via EasyConnect");
171 
172                 mWifiManager.connect(easyConnectNetworkId, new WifiManager.ActionListener() {
173                     @Override
174                     public void onSuccess() {
175                         if (DEBUG) Log.d(TAG, "EasyConnect: onSuccess");
176                     }
177 
178                     @Override
179                     public void onFailure(int reason) {
180                         if (DEBUG) Log.d(TAG, "EasyConnect: onFailure, reason = " + reason);
181                     }
182                 });
183             } else {
184                 mWifiManager.addNetwork(mWifiConfiguration);
185                 mWifiManager.connect(mWifiConfiguration, null);
186             }
187         }
188 
189         @Override
onDestroy()190         public void onDestroy() {
191             if (!isNetworkConnected()) {
192                 mWifiManager.disconnect();
193             }
194 
195             mConnectivityListener.stop();
196             mConnectivityListener.destroy();
197             mHandler.removeMessages(MSG_TIMEOUT);
198             super.onDestroy();
199         }
200 
201         @Override
onWifiListChanged()202         public void onWifiListChanged() {
203             List<AccessPoint> accessPointList = mConnectivityListener.getAvailableNetworks();
204             if (accessPointList != null) {
205                 for (AccessPoint accessPoint : accessPointList) {
206                     if (accessPoint != null && AccessPoint.convertToQuotedString(
207                             accessPoint.getSsidStr()).equals(mWifiConfiguration.SSID)) {
208                         inferConnectionStatus(accessPoint);
209                     }
210                 }
211             }
212         }
213 
inferConnectionStatus(AccessPoint accessPoint)214         private void inferConnectionStatus(AccessPoint accessPoint) {
215             WifiConfiguration configuration = accessPoint.getConfig();
216             if (configuration == null) {
217                 return;
218             }
219             if (configuration.getNetworkSelectionStatus().getNetworkSelectionStatus()
220                     == NETWORK_SELECTION_ENABLED) {
221                 NetworkCapabilities wifiNetworkCapabilities = getActiveWifiNetworkCapabilities();
222                 if (wifiNetworkCapabilities != null) {
223                     if (wifiNetworkCapabilities.hasCapability(
224                             NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
225                         notifyListener(StateMachine.RESULT_SUCCESS);
226                     } else if (wifiNetworkCapabilities.hasCapability(
227                             NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL)) {
228                         notifyListener(StateMachine.RESULT_CAPTIVE_PORTAL);
229                     }
230                 }
231             } else {
232                 switch (configuration.getNetworkSelectionStatus()
233                         .getNetworkSelectionDisableReason()) {
234                     case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
235                     case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD:
236                         mUserChoiceInfo.setConnectionFailedStatus(
237                                 UserChoiceInfo.ConnectionFailedStatus.AUTHENTICATION);
238                         break;
239                     case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
240                         mUserChoiceInfo.setConnectionFailedStatus(
241                                 UserChoiceInfo.ConnectionFailedStatus.REJECTED);
242                         break;
243                     case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
244                         notifyListener(StateMachine.RESULT_UNKNOWN_ERROR);
245                         break;
246                     default:
247                         mUserChoiceInfo.setConnectionFailedStatus(
248                                 UserChoiceInfo.ConnectionFailedStatus.UNKNOWN);
249                 }
250                 notifyListener(StateMachine.RESULT_FAILURE);
251                 accessPoint.clearConfig();
252             }
253         }
254 
notifyListener(int result)255         private void notifyListener(int result) {
256             if (mStateMachine.getCurrentState() instanceof ConnectState) {
257                 mStateMachine.getListener().onComplete(result);
258             }
259         }
260 
261         @Nullable
getActiveWifiNetworkCapabilities()262         private NetworkCapabilities getActiveWifiNetworkCapabilities() {
263             Network[] networks = mConnectivityManager.getAllNetworks();
264 
265             for (Network network : networks) {
266                 NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
267                 if (networkInfo.isConnected()
268                         && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
269                     return mConnectivityManager.getNetworkCapabilities(network);
270                 }
271             }
272             return null;
273         }
274 
getActiveWifiNetworkInfo()275         private NetworkInfo getActiveWifiNetworkInfo() {
276             Network[] networks = mConnectivityManager.getAllNetworks();
277 
278             for (Network network : networks) {
279                 NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
280                 if (networkInfo.isConnected()
281                         && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
282                     return networkInfo;
283                 }
284             }
285             return null;
286         }
287 
isNetworkConnected()288         private boolean isNetworkConnected() {
289             NetworkInfo netInfo = getActiveWifiNetworkInfo();
290             if (netInfo == null) {
291                 if (DEBUG) Log.d(TAG, "NetworkInfo is null; network is not connected");
292                 return false;
293             }
294 
295             if (DEBUG) Log.d(TAG, "NetworkInfo: " + netInfo.toString());
296             if (netInfo.isConnected() && netInfo.getType() == ConnectivityManager.TYPE_WIFI) {
297                 WifiInfo currentConnection = mWifiManager.getConnectionInfo();
298                 if (DEBUG) {
299                     Log.d(TAG, "Connected to "
300                             + ((currentConnection == null)
301                             ? "nothing" : currentConnection.getSSID()));
302                 }
303                 return currentConnection != null
304                         && currentConnection.getSSID().equals(mWifiConfiguration.SSID);
305             } else {
306                 if (DEBUG) Log.d(TAG, "Network is not connected");
307             }
308             return false;
309         }
310 
postTimeout()311         private void postTimeout() {
312             mHandler.removeMessages(MSG_TIMEOUT);
313             mHandler.sendEmptyMessageDelayed(MSG_TIMEOUT, CONNECTION_TIMEOUT);
314         }
315 
316         private static class MessageHandler extends Handler {
317 
318             private final WeakReference<ConnectToWifiFragment> mFragmentRef;
319 
MessageHandler(ConnectToWifiFragment fragment)320             MessageHandler(ConnectToWifiFragment fragment) {
321                 mFragmentRef = new WeakReference<>(fragment);
322             }
323 
324             @Override
handleMessage(Message msg)325             public void handleMessage(Message msg) {
326                 if (DEBUG) Log.d(TAG, "Timeout waiting on supplicant state change");
327 
328                 final ConnectToWifiFragment fragment = mFragmentRef.get();
329                 if (fragment == null) {
330                     return;
331                 }
332 
333                 if (fragment.isNetworkConnected()) {
334                     if (DEBUG) Log.d(TAG, "Fake timeout; we're actually connected");
335                     fragment.notifyListener(StateMachine.RESULT_SUCCESS);
336                 } else {
337                     if (DEBUG) Log.d(TAG, "Timeout is real; telling the listener");
338                     UserChoiceInfo userChoiceInfo = ViewModelProviders
339                             .of(fragment.getActivity()).get(UserChoiceInfo.class);
340                     userChoiceInfo.setConnectionFailedStatus(
341                             UserChoiceInfo.ConnectionFailedStatus.TIMEOUT);
342                     fragment.notifyListener(StateMachine.RESULT_FAILURE);
343                 }
344             }
345         }
346     }
347 }
348