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 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