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 { 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 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 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 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 103 public MessageHandler(ConnectToWifiFragment fragment) { 104 mFragmentRef = new WeakReference<>(fragment); 105 } 106 107 @Override 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 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 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 224 public void onDestroy() { 225 if (!mConnected) { 226 mWifiManager.disconnect(); 227 } 228 getActivity().unregisterReceiver(mReceiver); 229 mConnectivityListener.stop(); 230 mHandler.removeMessages(MSG_TIMEOUT); 231 super.onDestroy(); 232 } 233 234 @Override 235 public void onConnectivityChange(Intent intent) { 236 if (DEBUG) Log.d(TAG, "Connectivity changed"); 237 if (isNetworkConnected()) { 238 mConnected = true; 239 notifyListener(RESULT_SUCCESS); 240 } 241 } 242 243 private void notifyListener(int result) { 244 if (mListener != null && isResumed()) { 245 mListener.onConnectToWifiCompleted(result); 246 mListener = null; 247 } 248 } 249 250 private boolean isNetworkConnected() { 251 ConnectivityManager connMan = 252 (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); 253 NetworkInfo netInfo = connMan.getActiveNetworkInfo(); 254 if (netInfo == null) { 255 if (DEBUG) Log.d(TAG, "NetworkInfo is null; network is not connected"); 256 return false; 257 } 258 259 if (DEBUG) Log.d(TAG, "NetworkInfo: " + netInfo.toString()); 260 if (netInfo.isConnected() && netInfo.getType() == ConnectivityManager.TYPE_WIFI) { 261 WifiInfo currentConnection = mWifiManager.getConnectionInfo(); 262 if (DEBUG) { 263 Log.d(TAG, "Connected to " 264 + ((currentConnection == null) ? "nothing" : currentConnection.getSSID())); 265 } 266 if (currentConnection != null 267 && currentConnection.getSSID().equals(mWifiConfiguration.SSID)) { 268 return true; 269 } 270 } else { 271 if (DEBUG) Log.d(TAG, "Network is not connected"); 272 } 273 return false; 274 } 275 } 276