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.osu; 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.Network; 24 import android.net.NetworkInfo; 25 import android.net.wifi.WifiConfiguration; 26 import android.net.wifi.WifiInfo; 27 import android.net.wifi.WifiManager; 28 import android.net.wifi.WifiSsid; 29 import android.os.Handler; 30 import android.text.TextUtils; 31 import android.util.Log; 32 33 import java.io.IOException; 34 35 /** 36 * Responsible for setup/monitor on a Wi-Fi connection. 37 */ 38 public class NetworkConnection { 39 private static final String TAG = "OSU_NetworkConnection"; 40 41 private final WifiManager mWifiManager; 42 private final Callbacks mCallbacks; 43 private final int mNetworkId; 44 private boolean mConnected = false; 45 46 /** 47 * Callbacks on Wi-Fi connection state changes. 48 */ 49 public interface Callbacks { 50 /** 51 * Invoked when network connection is established with IP connectivity. 52 * 53 * @param network {@link Network} associated with the connected network. 54 */ 55 public void onConnected(Network network); 56 57 /** 58 * Invoked when the targeted network is disconnected. 59 */ 60 public void onDisconnected(); 61 62 /** 63 * Invoked when network connection is not established within the pre-defined timeout. 64 */ 65 public void onTimeout(); 66 } 67 68 /** 69 * Create an instance of {@link NetworkConnection} for the specified Wi-Fi network. 70 * The Wi-Fi network (specified by its SSID) will be added/enabled as part of this object 71 * creation. 72 * 73 * {@link #teardown} will need to be invoked once you're done with this connection, 74 * to remove the given Wi-Fi network from the framework. 75 * 76 * @param context The application context 77 * @param handler The handler to dispatch the processing of received broadcast intents 78 * @param ssid The SSID to connect to 79 * @param nai The network access identifier associated with the AP 80 * @param callbacks The callbacks to be invoked on network change events 81 * @throws IOException when failed to add/enable the specified Wi-Fi network 82 */ 83 public NetworkConnection(Context context, Handler handler, WifiSsid ssid, String nai, 84 Callbacks callbacks) throws IOException { 85 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 86 mCallbacks = callbacks; 87 mNetworkId = connect(ssid, nai); 88 89 // TODO(zqiu): setup alarm to timed out the connection attempt. 90 91 IntentFilter filter = new IntentFilter(); 92 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 93 BroadcastReceiver receiver = new BroadcastReceiver() { 94 @Override 95 public void onReceive(Context context, Intent intent) { 96 String action = intent.getAction(); 97 if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 98 handleNetworkStateChanged( 99 intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO), 100 intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)); 101 } 102 } 103 }; 104 // Provide a Handler so that the onReceive call will be run on the specified handler 105 // thread instead of the main thread. 106 context.registerReceiver(receiver, filter, null, handler); 107 } 108 109 /** 110 * Teardown the network connection by removing the network. 111 */ 112 public void teardown() { 113 mWifiManager.removeNetwork(mNetworkId); 114 } 115 116 /** 117 * Connect to a OSU Wi-Fi network specified by the given SSID. The security type of the Wi-Fi 118 * network is either open or OSEN (OSU Server-only authenticated layer 2 Encryption Network). 119 * When network access identifier is provided, OSEN is used. 120 * 121 * @param ssid The SSID to connect to 122 * @param nai Network access identifier of the network 123 * 124 * @return unique ID associated with the network 125 * @throws IOException 126 */ 127 private int connect(WifiSsid ssid, String nai) throws IOException { 128 WifiConfiguration config = new WifiConfiguration(); 129 config.SSID = "\"" + ssid.toString() + "\""; 130 if (TextUtils.isEmpty(nai)) { 131 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 132 } else { 133 // TODO(zqiu): configuration setup for OSEN. 134 } 135 int networkId = mWifiManager.addNetwork(config); 136 if (networkId < 0) { 137 throw new IOException("Failed to add OSU network"); 138 } 139 if (!mWifiManager.enableNetwork(networkId, true)) { 140 throw new IOException("Failed to enable OSU network"); 141 } 142 return networkId; 143 } 144 145 /** 146 * Handle network state changed events. 147 * 148 * @param networkInfo {@link NetworkInfo} indicating the current network state 149 * @param wifiInfo {@link WifiInfo} associated with the current network when connected 150 */ 151 private void handleNetworkStateChanged(NetworkInfo networkInfo, WifiInfo wifiInfo) { 152 if (networkInfo == null) { 153 Log.e(TAG, "NetworkInfo not provided for network state changed event"); 154 return; 155 } 156 switch (networkInfo.getDetailedState()) { 157 case CONNECTED: 158 handleConnectedEvent(wifiInfo); 159 break; 160 case DISCONNECTED: 161 handleDisconnectedEvent(); 162 break; 163 default: 164 Log.d(TAG, "Ignore uninterested state: " + networkInfo.getDetailedState()); 165 break; 166 } 167 } 168 169 /** 170 * Handle network connected event. 171 * 172 * @param wifiInfo {@link WifiInfo} associated with the current connection 173 */ 174 private void handleConnectedEvent(WifiInfo wifiInfo) { 175 if (mConnected) { 176 // No-op if already connected. 177 return; 178 } 179 if (wifiInfo == null) { 180 Log.e(TAG, "WifiInfo not provided for connected event"); 181 return; 182 } 183 if (wifiInfo.getNetworkId() != mNetworkId) { 184 return; 185 } 186 Network network = mWifiManager.getCurrentNetwork(); 187 if (network == null) { 188 Log.e(TAG, "Current network is not set"); 189 return; 190 } 191 mConnected = true; 192 mCallbacks.onConnected(network); 193 } 194 195 /** 196 * Handle network disconnected event. 197 */ 198 private void handleDisconnectedEvent() { 199 if (!mConnected) { 200 // No-op if not connected, most likely a disconnect event for a different network. 201 return; 202 } 203 mConnected = false; 204 mCallbacks.onDisconnected(); 205 } 206 } 207