1 /* 2 * Copyright (C) 2014 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; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.net.ConnectivityManager; 24 import android.net.NetworkInfo; 25 import android.net.wifi.SupplicantState; 26 import android.net.wifi.WifiConfiguration; 27 import android.net.wifi.WifiInfo; 28 import android.net.wifi.WifiManager; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.Message; 32 import android.util.Log; 33 34 import com.android.tv.settings.connectivity.setup.MessageWizardFragment; 35 36 import java.lang.ref.WeakReference; 37 38 /** 39 * Connects to the wifi network specified by the given configuration. 40 */ 41 public class ConnectToWifiFragment extends MessageWizardFragment 42 implements ConnectivityListener.Listener { 43 44 public interface Listener { onConnectToWifiCompleted(int reason)45 void onConnectToWifiCompleted(int reason); 46 } 47 48 private static final String TAG = "ConnectToWifiFragment"; 49 private static final boolean DEBUG = false; 50 51 public static final int RESULT_SUCCESS = 0; 52 public static final int RESULT_UNKNOWN_ERROR= 1; 53 public static final int RESULT_TIMEOUT = 2; 54 public static final int RESULT_BAD_AUTHENTICATION = 3; 55 public static final int RESULT_REJECTED_BY_AP = 4; 56 57 private static final String EXTRA_CONFIGURATION = "configuration"; 58 private static final int MSG_TIMEOUT = 1; 59 private static final int CONNECTION_TIMEOUT = 15000; 60 newInstance(String title, boolean showProgressIndicator, WifiConfiguration configuration)61 public static ConnectToWifiFragment newInstance(String title, boolean showProgressIndicator, 62 WifiConfiguration configuration) { 63 ConnectToWifiFragment fragment = new ConnectToWifiFragment(); 64 Bundle args = new Bundle(); 65 args.putParcelable(EXTRA_CONFIGURATION, configuration); 66 addArguments(args, title, showProgressIndicator); 67 fragment.setArguments(args); 68 return fragment; 69 } 70 71 private Listener mListener; 72 private ConnectivityListener mConnectivityListener; 73 private WifiConfiguration mWifiConfiguration; 74 private WifiManager mWifiManager; 75 private Handler mHandler; 76 private BroadcastReceiver mReceiver; 77 private boolean mWasAssociating; 78 private boolean mWasAssociated; 79 private boolean mWasHandshaking; 80 private boolean mConnected; 81 82 @Override onAttach(Context activity)83 public void onAttach(Context activity) { 84 if (activity instanceof Listener) { 85 mListener = (Listener) activity; 86 } else { 87 throw new IllegalArgumentException("Activity must implement " 88 + "ConnectToWifiFragment.Listener to use this fragment."); 89 } 90 super.onAttach(activity); 91 } 92 93 @Override onDetach()94 public void onDetach() { 95 super.onDetach(); 96 mListener = null; 97 } 98 99 private static class MessageHandler extends Handler { 100 101 private final WeakReference<ConnectToWifiFragment> mFragmentRef; 102 MessageHandler(ConnectToWifiFragment fragment)103 public MessageHandler(ConnectToWifiFragment fragment) { 104 mFragmentRef = new WeakReference<>(fragment); 105 } 106 107 @Override handleMessage(Message msg)108 public void handleMessage(Message msg) { 109 if (DEBUG) Log.d(TAG, "Timeout waiting on supplicant state change"); 110 111 final ConnectToWifiFragment fragment = mFragmentRef.get(); 112 if (fragment == null) { 113 return; 114 } 115 116 if (fragment.isNetworkConnected()) { 117 if (DEBUG) Log.d(TAG, "Fake timeout; we're actually connected"); 118 fragment.mConnected = true; 119 fragment.notifyListener(RESULT_SUCCESS); 120 } else { 121 if (DEBUG) Log.d(TAG, "Timeout is real; telling the listener"); 122 fragment.notifyListener(RESULT_TIMEOUT); 123 } 124 } 125 } 126 127 @Override onCreate(Bundle icicle)128 public void onCreate(Bundle icicle) { 129 super.onCreate(icicle); 130 131 mConnectivityListener = new ConnectivityListener(getActivity(), this); 132 mWifiConfiguration = getArguments().getParcelable(EXTRA_CONFIGURATION); 133 mWifiManager = ((WifiManager) getActivity().getSystemService(Context.WIFI_SERVICE)); 134 mHandler = new MessageHandler(this); 135 136 IntentFilter filter = new IntentFilter(); 137 filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 138 mReceiver = new BroadcastReceiver() { 139 @Override 140 public void onReceive(Context context, Intent intent) { 141 if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(intent.getAction())) { 142 SupplicantState state = intent.getParcelableExtra( 143 WifiManager.EXTRA_NEW_STATE); 144 if (DEBUG) { 145 Log.d(TAG, "Got supplicant state: " + state.name()); 146 } 147 switch (state) { 148 case ASSOCIATING: 149 mWasAssociating = true; 150 break; 151 case ASSOCIATED: 152 mWasAssociated = true; 153 break; 154 case COMPLETED: 155 // this just means the supplicant has connected, now 156 // we wait for the rest of the framework to catch up 157 break; 158 case DISCONNECTED: 159 case DORMANT: 160 if (mWasAssociated || mWasHandshaking) { 161 notifyListener(mWasHandshaking ? RESULT_BAD_AUTHENTICATION 162 : RESULT_UNKNOWN_ERROR); 163 } 164 break; 165 case INTERFACE_DISABLED: 166 case UNINITIALIZED: 167 notifyListener(RESULT_UNKNOWN_ERROR); 168 break; 169 case FOUR_WAY_HANDSHAKE: 170 case GROUP_HANDSHAKE: 171 mWasHandshaking = true; 172 break; 173 case INACTIVE: 174 if (mWasAssociating && !mWasAssociated) { 175 // If we go inactive after 'associating' without ever having 176 // been 'associated', the AP(s) must have rejected us. 177 notifyListener(RESULT_REJECTED_BY_AP); 178 break; 179 } 180 case INVALID: 181 case SCANNING: 182 default: 183 return; 184 } 185 mHandler.removeMessages(MSG_TIMEOUT); 186 mHandler.sendEmptyMessageDelayed(MSG_TIMEOUT, CONNECTION_TIMEOUT); 187 } 188 } 189 }; 190 getActivity().registerReceiver(mReceiver, filter); 191 mConnectivityListener.start(); 192 } 193 194 @Override onResume()195 public void onResume() { 196 super.onResume(); 197 if (isNetworkConnected()) { 198 mConnected = true; 199 notifyListener(RESULT_SUCCESS); 200 } else { 201 int networkId = mWifiManager.addNetwork(mWifiConfiguration); 202 if (networkId == -1) { 203 if (DEBUG) { 204 Log.d(TAG, "Failed to add network!"); 205 } 206 notifyListener(RESULT_UNKNOWN_ERROR); 207 } else if (!mWifiManager.enableNetwork(networkId, true)) { 208 if (DEBUG) { 209 Log.d(TAG, "Failed to enable network id " + networkId + "!"); 210 } 211 notifyListener(RESULT_UNKNOWN_ERROR); 212 } else if (!mWifiManager.reconnect()) { 213 if (DEBUG) { 214 Log.d(TAG, "Failed to reconnect!"); 215 } 216 notifyListener(RESULT_UNKNOWN_ERROR); 217 } else { 218 mHandler.sendEmptyMessageDelayed(MSG_TIMEOUT, CONNECTION_TIMEOUT); 219 } 220 } 221 } 222 223 @Override onDestroy()224 public void onDestroy() { 225 if (!mConnected) { 226 mWifiManager.disconnect(); 227 } 228 getActivity().unregisterReceiver(mReceiver); 229 mConnectivityListener.stop(); 230 mConnectivityListener.destroy(); 231 mHandler.removeMessages(MSG_TIMEOUT); 232 super.onDestroy(); 233 } 234 235 @Override onConnectivityChange()236 public void onConnectivityChange() { 237 if (DEBUG) Log.d(TAG, "Connectivity changed"); 238 if (!isResumed()) { 239 return; 240 } 241 if (isNetworkConnected()) { 242 mConnected = true; 243 notifyListener(RESULT_SUCCESS); 244 } 245 } 246 notifyListener(int result)247 private void notifyListener(int result) { 248 if (mListener != null && isResumed()) { 249 mListener.onConnectToWifiCompleted(result); 250 mListener = null; 251 } 252 } 253 isNetworkConnected()254 private boolean isNetworkConnected() { 255 ConnectivityManager connMan = 256 (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); 257 NetworkInfo netInfo = connMan.getActiveNetworkInfo(); 258 if (netInfo == null) { 259 if (DEBUG) Log.d(TAG, "NetworkInfo is null; network is not connected"); 260 return false; 261 } 262 263 if (DEBUG) Log.d(TAG, "NetworkInfo: " + netInfo.toString()); 264 if (netInfo.isConnected() && netInfo.getType() == ConnectivityManager.TYPE_WIFI) { 265 WifiInfo currentConnection = mWifiManager.getConnectionInfo(); 266 if (DEBUG) { 267 Log.d(TAG, "Connected to " 268 + ((currentConnection == null) ? "nothing" : currentConnection.getSSID())); 269 } 270 if (currentConnection != null 271 && currentConnection.getSSID().equals(mWifiConfiguration.SSID)) { 272 return true; 273 } 274 } else { 275 if (DEBUG) Log.d(TAG, "Network is not connected"); 276 } 277 return false; 278 } 279 } 280