/* * 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(); } }