• 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                         wifiNetworkCapabilities.hasCapability(
226                             NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
227                         notifyListener(StateMachine.RESULT_SUCCESS);
228                     } else if (wifiNetworkCapabilities.hasCapability(
229                             NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL)) {
230                         notifyListener(StateMachine.RESULT_CAPTIVE_PORTAL);
231                     }
232 
233                 }
234             } else {
235                 switch (configuration.getNetworkSelectionStatus()
236                         .getNetworkSelectionDisableReason()) {
237                     case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
238                     case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD:
239                         mUserChoiceInfo.setConnectionFailedStatus(
240                                 UserChoiceInfo.ConnectionFailedStatus.AUTHENTICATION);
241                         break;
242                     case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
243                         mUserChoiceInfo.setConnectionFailedStatus(
244                                 UserChoiceInfo.ConnectionFailedStatus.REJECTED);
245                         break;
246                     case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
247                         notifyListener(StateMachine.RESULT_UNKNOWN_ERROR);
248                         break;
249                     default:
250                         mUserChoiceInfo.setConnectionFailedStatus(
251                                 UserChoiceInfo.ConnectionFailedStatus.UNKNOWN);
252                 }
253                 notifyListener(StateMachine.RESULT_FAILURE);
254                 accessPoint.clearConfig();
255             }
256         }
257 
notifyListener(int result)258         private void notifyListener(int result) {
259             if (mStateMachine.getCurrentState() instanceof ConnectState) {
260                 mStateMachine.getListener().onComplete(result);
261             }
262         }
263 
264         @Nullable
getActiveWifiNetworkCapabilities()265         private NetworkCapabilities getActiveWifiNetworkCapabilities() {
266             Network[] networks = mConnectivityManager.getAllNetworks();
267 
268             for (Network network : networks) {
269                 NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
270                 if (networkInfo.isConnected()
271                         && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
272                     return mConnectivityManager.getNetworkCapabilities(network);
273                 }
274             }
275             return null;
276         }
277 
getActiveWifiNetworkInfo()278         private NetworkInfo getActiveWifiNetworkInfo() {
279             Network[] networks = mConnectivityManager.getAllNetworks();
280 
281             for (Network network : networks) {
282                 NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
283                 if (networkInfo.isConnected()
284                         && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
285                     return networkInfo;
286                 }
287             }
288             return null;
289         }
290 
isNetworkConnected()291         private boolean isNetworkConnected() {
292             NetworkInfo netInfo = getActiveWifiNetworkInfo();
293             if (netInfo == null) {
294                 if (DEBUG) Log.d(TAG, "NetworkInfo is null; network is not connected");
295                 return false;
296             }
297 
298             if (DEBUG) Log.d(TAG, "NetworkInfo: " + netInfo.toString());
299             if (netInfo.isConnected() && netInfo.getType() == ConnectivityManager.TYPE_WIFI) {
300                 WifiInfo currentConnection = mWifiManager.getConnectionInfo();
301                 if (DEBUG) {
302                     Log.d(TAG, "Connected to "
303                             + ((currentConnection == null)
304                             ? "nothing" : currentConnection.getSSID()));
305                 }
306                 return currentConnection != null
307                         && currentConnection.getSSID().equals(mWifiConfiguration.SSID);
308             } else {
309                 if (DEBUG) Log.d(TAG, "Network is not connected");
310             }
311             return false;
312         }
313 
postTimeout()314         private void postTimeout() {
315             mHandler.removeMessages(MSG_TIMEOUT);
316             mHandler.sendEmptyMessageDelayed(MSG_TIMEOUT, CONNECTION_TIMEOUT);
317         }
318 
319         private static class MessageHandler extends Handler {
320 
321             private final WeakReference<ConnectToWifiFragment> mFragmentRef;
322 
MessageHandler(ConnectToWifiFragment fragment)323             MessageHandler(ConnectToWifiFragment fragment) {
324                 mFragmentRef = new WeakReference<>(fragment);
325             }
326 
327             @Override
handleMessage(Message msg)328             public void handleMessage(Message msg) {
329                 if (DEBUG) Log.d(TAG, "Timeout waiting on supplicant state change");
330 
331                 final ConnectToWifiFragment fragment = mFragmentRef.get();
332                 if (fragment == null) {
333                     return;
334                 }
335 
336                 if (fragment.isNetworkConnected()) {
337                     if (DEBUG) Log.d(TAG, "Fake timeout; we're actually connected");
338                     fragment.notifyListener(StateMachine.RESULT_SUCCESS);
339                 } else {
340                     if (DEBUG) Log.d(TAG, "Timeout is real; telling the listener");
341                     UserChoiceInfo userChoiceInfo = ViewModelProviders
342                             .of(fragment.getActivity()).get(UserChoiceInfo.class);
343                     userChoiceInfo.setConnectionFailedStatus(
344                             UserChoiceInfo.ConnectionFailedStatus.TIMEOUT);
345                     fragment.notifyListener(StateMachine.RESULT_FAILURE);
346                 }
347             }
348         }
349     }
350 }
351