blob: 9f5b929e96d32b862dbda215f6d15988279a0eec [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.osu;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiSsid;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import java.io.IOException;
/**
* Responsible for setup/monitor on a Wi-Fi connection.
*/
public class NetworkConnection {
private static final String TAG = "OSU_NetworkConnection";
private final WifiManager mWifiManager;
private final Callbacks mCallbacks;
private final int mNetworkId;
private boolean mConnected = false;
/**
* Callbacks on Wi-Fi connection state changes.
*/
public interface Callbacks {
/**
* Invoked when network connection is established with IP connectivity.
*
* @param network {@link Network} associated with the connected network.
*/
public void onConnected(Network network);
/**
* Invoked when the targeted network is disconnected.
*/
public void onDisconnected();
/**
* Invoked when network connection is not established within the pre-defined timeout.
*/
public void onTimeout();
}
/**
* Create an instance of {@link NetworkConnection} for the specified Wi-Fi network.
* The Wi-Fi network (specified by its SSID) will be added/enabled as part of this object
* creation.
*
* {@link #teardown} will need to be invoked once you're done with this connection,
* to remove the given Wi-Fi network from the framework.
*
* @param context The application context
* @param handler The handler to dispatch the processing of received broadcast intents
* @param ssid The SSID to connect to
* @param nai The network access identifier associated with the AP
* @param callbacks The callbacks to be invoked on network change events
* @throws IOException when failed to add/enable the specified Wi-Fi network
*/
public NetworkConnection(Context context, Handler handler, WifiSsid ssid, String nai,
Callbacks callbacks) throws IOException {
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mCallbacks = callbacks;
mNetworkId = connect(ssid, nai);
// TODO(zqiu): setup alarm to timed out the connection attempt.
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
handleNetworkStateChanged(
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO),
intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO));
}
}
};
// Provide a Handler so that the onReceive call will be run on the specified handler
// thread instead of the main thread.
context.registerReceiver(receiver, filter, null, handler);
}
/**
* Teardown the network connection by removing the network.
*/
public void teardown() {
mWifiManager.removeNetwork(mNetworkId);
}
/**
* Connect to a OSU Wi-Fi network specified by the given SSID. The security type of the Wi-Fi
* network is either open or OSEN (OSU Server-only authenticated layer 2 Encryption Network).
* When network access identifier is provided, OSEN is used.
*
* @param ssid The SSID to connect to
* @param nai Network access identifier of the network
*
* @return unique ID associated with the network
* @throws IOException
*/
private int connect(WifiSsid ssid, String nai) throws IOException {
WifiConfiguration config = new WifiConfiguration();
config.SSID = "\"" + ssid.toString() + "\"";
if (TextUtils.isEmpty(nai)) {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
} else {
// TODO(zqiu): configuration setup for OSEN.
}
int networkId = mWifiManager.addNetwork(config);
if (networkId < 0) {
throw new IOException("Failed to add OSU network");
}
if (!mWifiManager.enableNetwork(networkId, true)) {
throw new IOException("Failed to enable OSU network");
}
return networkId;
}
/**
* Handle network state changed events.
*
* @param networkInfo {@link NetworkInfo} indicating the current network state
* @param wifiInfo {@link WifiInfo} associated with the current network when connected
*/
private void handleNetworkStateChanged(NetworkInfo networkInfo, WifiInfo wifiInfo) {
if (networkInfo == null) {
Log.e(TAG, "NetworkInfo not provided for network state changed event");
return;
}
switch (networkInfo.getDetailedState()) {
case CONNECTED:
handleConnectedEvent(wifiInfo);
break;
case DISCONNECTED:
handleDisconnectedEvent();
break;
default:
Log.d(TAG, "Ignore uninterested state: " + networkInfo.getDetailedState());
break;
}
}
/**
* Handle network connected event.
*
* @param wifiInfo {@link WifiInfo} associated with the current connection
*/
private void handleConnectedEvent(WifiInfo wifiInfo) {
if (mConnected) {
// No-op if already connected.
return;
}
if (wifiInfo == null) {
Log.e(TAG, "WifiInfo not provided for connected event");
return;
}
if (wifiInfo.getNetworkId() != mNetworkId) {
return;
}
Network network = mWifiManager.getCurrentNetwork();
if (network == null) {
Log.e(TAG, "Current network is not set");
return;
}
mConnected = true;
mCallbacks.onConnected(network);
}
/**
* Handle network disconnected event.
*/
private void handleDisconnectedEvent() {
if (!mConnected) {
// No-op if not connected, most likely a disconnect event for a different network.
return;
}
mConnected = false;
mCallbacks.onDisconnected();
}
}