diff options
Diffstat (limited to 'app/src/fil/libre/repwifiapp/fwproxies/RepWifiNetworkAgent.java')
-rw-r--r-- | app/src/fil/libre/repwifiapp/fwproxies/RepWifiNetworkAgent.java | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/app/src/fil/libre/repwifiapp/fwproxies/RepWifiNetworkAgent.java b/app/src/fil/libre/repwifiapp/fwproxies/RepWifiNetworkAgent.java new file mode 100644 index 0000000..bab5c5d --- /dev/null +++ b/app/src/fil/libre/repwifiapp/fwproxies/RepWifiNetworkAgent.java @@ -0,0 +1,581 @@ +// +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** +// +// This file is derivative work, inspired by the original class definition: +// "com.android.server.wifi.WifiStateMachine$WifiNetworkAgent.java" +// as found in version 6.0 of the Android Operating System. +// Following is the original copyright notice: +/* + * Copyright (C) 2010 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 fil.libre.repwifiapp.fwproxies; + +import android.content.Context; +import android.net.NetworkInfo; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.util.Log; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + +public class RepWifiNetworkAgent extends Handler { + + public final int netId; + + private Messenger myMessenger = null; + + private volatile AsyncChannelProxy mAsyncChannel; + private final String LOG_TAG; + private static final boolean DBG = true; + private static final boolean VDBG = true; + private final Context mContext; + private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>(); + private volatile long mLastBwRefreshTime = 0; + private static final long BW_REFRESH_MIN_WIN_MS = 500; + private boolean mPollLceScheduled = false; + private AtomicBoolean mPollLcePending = new AtomicBoolean(false); + + /* as in com.android.internal.util.Protocol.BASE_NETWORK_AGENT; */ + private static final int BASE = 0x00081000; + + /** + * Sent by ConnectivityService to the NetworkAgent to inform it of + * suspected connectivity problems on its network. The NetworkAgent + * should take steps to verify and correct connectivity. + */ + public static final int CMD_SUSPECT_BAD = BASE; + + /** + * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to + * ConnectivityService to pass the current NetworkInfo (connection state). + * Sent when the NetworkInfo changes, mainly due to change of state. + * obj = NetworkInfo + */ + public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1; + + /** + * Sent by the NetworkAgent to ConnectivityService to pass the current + * NetworkCapabilties. + * obj = NetworkCapabilities + */ + public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2; + + /** + * Sent by the NetworkAgent to ConnectivityService to pass the current + * NetworkProperties. + * obj = NetworkProperties + */ + public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3; + + public static final int WIFI_BASE_SCORE = 60; + + /** + * Sent by the NetworkAgent to ConnectivityService to pass the current + * network score. + * obj = network score Integer + */ + public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4; + + /** + * Sent by the NetworkAgent to ConnectivityService to add new UID ranges + * to be forced into this Network. For VPNs only. + * obj = UidRange[] to forward + */ + public static final int EVENT_UID_RANGES_ADDED = BASE + 5; + + /** + * Sent by the NetworkAgent to ConnectivityService to remove UID ranges + * from being forced into this Network. For VPNs only. + * obj = UidRange[] to stop forwarding + */ + public static final int EVENT_UID_RANGES_REMOVED = BASE + 6; + + /** + * Sent by ConnectivityService to the NetworkAgent to inform the agent of + * the + * networks status - whether we could use the network or could not, due to + * either a bad network configuration (no internet link) or captive portal. + * + * arg1 = either {@code VALID_NETWORK} or {@code INVALID_NETWORK} + */ + public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7; + + public static final int VALID_NETWORK = 1; + public static final int INVALID_NETWORK = 2; + + /** + * Sent by the NetworkAgent to ConnectivityService to indicate this network + * was + * explicitly selected. This should be sent before the NetworkInfo is marked + * CONNECTED so it can be given special treatment at that time. + * + * obj = boolean indicating whether to use this network even if unvalidated + */ + public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8; + + /** + * Sent by ConnectivityService to the NetworkAgent to inform the agent of + * whether the network should in the future be used even if not validated. + * This decision is made by the user, but it is the network transport's + * responsibility to remember it. + * + * arg1 = 1 if true, 0 if false + */ + public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9; + + /** + * Sent by ConnectivityService to the NetworkAgent to inform the agent to + * pull + * the underlying network connection for updated bandwidth information. + */ + public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10; + + /** + * Sent by ConnectivityService to the NetworkAgent to request that the + * specified packet be sent + * periodically on the given interval. + * + * arg1 = the slot number of the keepalive to start + * arg2 = interval in seconds + * obj = KeepalivePacketData object describing the data to be sent + * + * Also used internally by ConnectivityService / KeepaliveTracker, with + * different semantics. + */ + public static final int CMD_START_PACKET_KEEPALIVE = BASE + 11; + + /** + * Requests that the specified keepalive packet be stopped. + * + * arg1 = slot number of the keepalive to stop. + * + * Also used internally by ConnectivityService / KeepaliveTracker, with + * different semantics. + */ + public static final int CMD_STOP_PACKET_KEEPALIVE = BASE + 12; + + /** + * Sent by the NetworkAgent to ConnectivityService to provide status on a + * packet keepalive + * request. This may either be the reply to a CMD_START_PACKET_KEEPALIVE, or + * an asynchronous + * error notification. + * + * This is also sent by KeepaliveTracker to the app's + * ConnectivityManager.PacketKeepalive to + * so that the app's PacketKeepaliveCallback methods can be called. + * + * arg1 = slot number of the keepalive + * arg2 = error code + */ + public static final int EVENT_PACKET_KEEPALIVE = BASE + 13; + + /** + * Sent by ConnectivityService to inform this network transport of signal + * strength thresholds + * that when crossed should trigger a system wakeup and a + * NetworkCapabilities update. + * + * obj = int[] describing signal strength thresholds. + */ + public static final int CMD_SET_SIGNAL_STRENGTH_THRESHOLDS = BASE + 14; + + /** + * Sent by ConnectivityService to the NeworkAgent to inform the agent to + * avoid + * automatically reconnecting to this network (e.g. via autojoin). Happens + * when user selects "No" option on the "Stay connected?" dialog box. + */ + public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15; + + public RepWifiNetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, + NetworkCapabilitiesProxy nc, LinkPropertiesProxy lp, int score) { + + super(looper); + + LOG_TAG = logTag; + mContext = context; + if (ni == null || nc == null || lp == null) { + throw new IllegalArgumentException(); + } + + if (VDBG) + log("Registering NetworkAgent"); + + ConnectivityManagerProxy cm = new ConnectivityManagerProxy(mContext); + + myMessenger = new Messenger(this); + netId = cm.registerNetworkAgent(myMessenger, new NetworkInfoProxy(ni), + new LinkPropertiesProxy(lp), new NetworkCapabilitiesProxy(nc), score); + + } + + public boolean isChannellConnected() { + return (mAsyncChannel != null); + } + + @Override + public void handleMessage(Message msg) { + + switch (msg.what) { + case AsyncChannelProxy.CMD_CHANNEL_FULL_CONNECTION: { + if (mAsyncChannel != null) { + log("Received new connection while already connected!"); + } else { + if (VDBG) + log("NetworkAgent fully connected"); + AsyncChannelProxy ac = new AsyncChannelProxy(); + ac.connected(null, this, msg.replyTo); + ac.replyToMessage(msg, AsyncChannelProxy.CMD_CHANNEL_FULLY_CONNECTED, + AsyncChannelProxy.STATUS_SUCCESSFUL); + synchronized (mPreConnectedQueue) { + mAsyncChannel = ac; + for (Message m : mPreConnectedQueue) { + ac.sendMessage(m); + } + mPreConnectedQueue.clear(); + } + } + break; + } + case AsyncChannelProxy.CMD_CHANNEL_DISCONNECT: { + if (VDBG) + log("CMD_CHANNEL_DISCONNECT"); + if (mAsyncChannel != null) + mAsyncChannel.disconnect(); + break; + } + case AsyncChannelProxy.CMD_CHANNEL_DISCONNECTED: { + if (DBG) + log("NetworkAgent channel lost"); + // let the client know CS is done with us. + + synchronized (mPreConnectedQueue) { + mAsyncChannel = null; + } + break; + } + case CMD_SUSPECT_BAD: { + log("Unhandled Message " + msg); + break; + } + case CMD_REQUEST_BANDWIDTH_UPDATE: { + long currentTimeMs = System.currentTimeMillis(); + if (VDBG) { + log("CMD_REQUEST_BANDWIDTH_UPDATE request received."); + } + if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) { + mPollLceScheduled = false; + if (mPollLcePending.getAndSet(true) == false) { + + shouldCallUninimplementedMethod("pollLceData()"); + + } + } else { + // deliver the request at a later time rather than discard it + // completely. + if (!mPollLceScheduled) { + long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS - currentTimeMs + 1; + mPollLceScheduled = sendEmptyMessageDelayed(CMD_REQUEST_BANDWIDTH_UPDATE, + waitTime); + } + } + break; + } + case CMD_REPORT_NETWORK_STATUS: { + if (VDBG) { + log("CMD_REPORT_NETWORK_STATUS(" + + (msg.arg1 == VALID_NETWORK ? "VALID)" : "INVALID)")); + } + shouldCallUninimplementedMethod("shounetworkStatus(msg.arg1)"); + break; + } + case CMD_SAVE_ACCEPT_UNVALIDATED: { + shouldCallUninimplementedMethod("saveAcceptUnvalidated(msg.arg1 != 0)"); + + break; + } + case CMD_START_PACKET_KEEPALIVE: { + shouldCallUninimplementedMethod("startPacketKeepalive(msg)"); + break; + } + case CMD_STOP_PACKET_KEEPALIVE: { + shouldCallUninimplementedMethod("stopPacketKeepalive(msg)"); + + break; + } + + case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: { + ArrayList<Integer> thresholds = ((Bundle) msg.obj).getIntegerArrayList("thresholds"); + int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0]; + for (int i = 0; i < intThresholds.length; i++) { + intThresholds[i] = thresholds.get(i); + } + shouldCallUninimplementedMethod("setSignalStrengthThresholds(intThresholds)"); + break; + } + case CMD_PREVENT_AUTOMATIC_RECONNECT: { + shouldCallUninimplementedMethod("preventAutomaticReconnect()"); + break; + } + default: { + String rep = ""; + if (msg.replyTo != null) { + rep = msg.replyTo.toString(); + } + log("Received unhandled message: what = " + msg.what + " replyTo: " + rep); + } + } + } + + private void queueOrSendMessage(int what, Object obj) { + queueOrSendMessage(what, 0, 0, obj); + } + + /* + * private void queueOrSendMessage(int what, int arg1, int arg2) { + * queueOrSendMessage(what, arg1, arg2, null); + * } + */ + + private void queueOrSendMessage(int what, int arg1, int arg2, Object obj) { + if (VDBG) + log("Send or queue message; what=" + what); + Message msg = Message.obtain(); + msg.what = what; + msg.arg1 = arg1; + msg.arg2 = arg2; + msg.obj = obj; + msg.replyTo = this.myMessenger; + + queueOrSendMessage(msg); + } + + private void queueOrSendMessage(Message msg) { + synchronized (mPreConnectedQueue) { + if (mAsyncChannel != null) { + if (VDBG) + log("Actually sending message " + msg); + mAsyncChannel.sendMessage(msg); + } else { + mPreConnectedQueue.add(msg); + } + } + } + + /** + * Called by the bearer code when it has new LinkProperties data. + */ + public void sendLinkProperties(LinkPropertiesProxy linkProperties) { + queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, + new LinkPropertiesProxy(linkProperties).inner); + } + + /** + * Called by the bearer code when it has new NetworkInfo data. + */ + public void sendNetworkInfo(NetworkInfo networkInfo) { + queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, + new NetworkInfoProxy(networkInfo).getNetworkInfo()); + } + + /** + * Called by the bearer code when it has new NetworkCapabilities data. + */ + public void sendNetworkCapabilities(NetworkCapabilitiesProxy networkCapabilities) { + mPollLcePending.set(false); + mLastBwRefreshTime = System.currentTimeMillis(); + queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, new NetworkCapabilitiesProxy( + networkCapabilities).inner); + } + + /** + * Called by the bearer code when it has a new score for this network. + */ + public void sendNetworkScore(int score) { + if (score < 0) { + throw new IllegalArgumentException("Score must be >= 0"); + } + queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, Integer.valueOf(score)); + } + + protected void log(String s) { + Log.d(LOG_TAG, "NetworkAgent: " + s); + } + + private void shouldCallUninimplementedMethod(String methodName) { + String msg = "[WRN] Should be calling " + methodName + + " but the method is not implemented by the proxy.."; + Log.w(LOG_TAG, msg); + } + + /* + * A proxy for package com.android.internal.util.AsyncChannel; + * Mererly replicates constants and functions needed by the NetworkAgent to + * communicate with the other side of the Handler + */ + private static class AsyncChannelProxy extends FrameworkProxy { + + // as in com.android.internal.util.Protocol.BASE_SYSTEM_ASYNC_CHANNEL; + private static final int BASE = 0x00011000; + + /** Successful status always 0, !0 is an unsuccessful status */ + public static final int STATUS_SUCCESSFUL = 0; + + /** + * Command typically sent when after receiving the + * CMD_CHANNEL_HALF_CONNECTED. + * This is used to initiate a long term connection with the destination + * and + * typically the destination will reply with + * CMD_CHANNEL_FULLY_CONNECTED. + * + * msg.replyTo = srcMessenger. + */ + public static final int CMD_CHANNEL_FULL_CONNECTION = BASE + 1; + + /** + * Command typically sent after the destination receives a + * CMD_CHANNEL_FULL_CONNECTION. + * This signifies the acceptance or rejection of the channel by the + * sender. + * + * msg.arg1 == 0 : Accept connection + * : All other values signify the destination rejected the connection + * and {@link AsyncChannel#disconnect} would typically be called. + */ + public static final int CMD_CHANNEL_FULLY_CONNECTED = BASE + 2; + + /** + * Command sent when one side or the other wishes to disconnect. The + * sender + * may or may not be able to receive a reply depending upon the protocol + * and + * the state of the connection. The receiver should call + * {@link AsyncChannel#disconnect} to close its side of the channel and + * it will receive a CMD_CHANNEL_DISCONNECTED + * when the channel is closed. + * + * msg.replyTo = messenger that is disconnecting + */ + public static final int CMD_CHANNEL_DISCONNECT = BASE + 3; + + /** + * Command sent when the channel becomes disconnected. This is sent when + * the + * channel is forcibly disconnected by the system or as a reply to + * CMD_CHANNEL_DISCONNECT. + * + * msg.arg1 == 0 : STATUS_SUCCESSFUL + * 1 : STATUS_BINDING_UNSUCCESSFUL + * 2 : STATUS_SEND_UNSUCCESSFUL + * : All other values signify failure and the channel state is + * indeterminate + * msg.obj == the AsyncChannel + * msg.replyTo = messenger disconnecting or null if it was never + * connected. + */ + public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4; + + /* + * Following is a block of unused system constants, kept here for future + * reference + *//** + * Command sent when the channel is half connected. Half connected + * means that the channel can be used to send commends to the + * destination + * but the destination is unaware that the channel exists. The first + * command sent to the destination is typically + * CMD_CHANNEL_FULL_CONNECTION if + * it is desired to establish a long term connection, but any command + * maybe + * sent. + * + * msg.arg1 == 0 : STATUS_SUCCESSFUL + * 1 : STATUS_BINDING_UNSUCCESSFUL + * msg.obj == the AsyncChannel + * msg.replyTo == dstMessenger if successful + */ + /* + * public static final int CMD_CHANNEL_HALF_CONNECTED = BASE + 0; + * + * private static final int CMD_TO_STRING_COUNT = + * CMD_CHANNEL_DISCONNECTED - BASE + 1; + *//** Error attempting to bind on a connect */ + /* + * public static final int STATUS_BINDING_UNSUCCESSFUL = 1; + *//** Error attempting to send a message */ + /* + * public static final int STATUS_SEND_UNSUCCESSFUL = 2; + *//** CMD_FULLY_CONNECTED refused because a connection already exists */ + /* + * public static final int + * STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED = 3; + *//** Error indicating abnormal termination of destination messenger */ + /* + * public static final int STATUS_REMOTE_DISCONNECTION = 4; + */ + + public AsyncChannelProxy() { + createInnerObject(null); + } + + @Override + protected String getInnerClassName() { + return "com.android.internal.util.AsyncChannel"; + } + + public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) { + invokeMethod("connected", getTypesArray(Context.class, Handler.class, Messenger.class), + srcContext, srcHandler, dstMessenger); + } + + public void disconnect() { + invokeMethod("disconnect", null); + } + + public void sendMessage(Message msg) { + invokeMethod("sendMessage", Message.class, msg); + } + + public void replyToMessage(Message srcMsg, int what, int arg1) { + invokeMethod("replyToMessage", getTypesArray(Message.class, int.class, int.class), + srcMsg, what, arg1); + } + + } + +} |