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