summaryrefslogtreecommitdiff
path: root/java/com/android/softap/SoftApManageService.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/softap/SoftApManageService.java')
-rw-r--r--java/com/android/softap/SoftApManageService.java394
1 files changed, 394 insertions, 0 deletions
diff --git a/java/com/android/softap/SoftApManageService.java b/java/com/android/softap/SoftApManageService.java
new file mode 100644
index 0000000..8d6ec25
--- /dev/null
+++ b/java/com/android/softap/SoftApManageService.java
@@ -0,0 +1,394 @@
+package com.android.softap;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiClient;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.softap.model.ClientInfo;
+import com.android.softap.oui.MACData;
+import com.android.softap.oui.MACDataHelper;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
+
+public class SoftApManageService extends Service implements WifiManager.SoftApCallback {
+
+ private static final String IP_NEIGHBOR_COMMAND_FORMAT = "ip neigh show dev %s";
+ private static final int COLUMN_IP_ADDRESS = 0;
+ private static final int COLUMN_MAC_ADDRESS = 2;
+
+ private static final int MSG_STATUS_CHANGED = 1;
+ private static final int MSG_UPDATE_CLIENT_LIST = 2;
+
+ public static final int STATUS_NORMAL = 0;
+ public static final int STATUS_CLIENTS_REFRESHING = 1;
+ public static final int STATUS_CLIENTS_REFRESH_DONE = 2;
+ public static final int STATUS_ERROR = 3;
+ public static final int STATUS_BLOCK_LIST_UPDATED = 4;
+
+ private static File BLOCKED_MAC_ADDRESS_FILE;
+
+ private INetworkManagementService mNetworkManagementService;
+ private WifiManager mWifiManager;
+
+ private String mInterfaceName;
+ private final ArrayList<String> mBlockedMACList = new ArrayList<>();
+ private final ArrayList<StatusListener> mListeners = new ArrayList<>();
+ private final HashMap<String, ClientInfo> mClients = new HashMap<>();
+
+ private Thread mClientUpdateThread;
+ private Handler mHandler;
+
+ /* Use this receiver to get interface name and register callback */
+ private final BroadcastReceiver mApStatusReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(intent.getAction())) {
+ mInterfaceName = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME);
+ unregisterReceiver(this);
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler),SoftApManageService.this);
+ }
+ }
+ };
+
+ public SoftApManageService() {
+ }
+
+ private Thread createClientUpdateThread(final List<WifiClient> clients) {
+ return new Thread() {
+ @Override
+ public void run() {
+ try {
+ IUpdateClientList(clients);
+ sendStatusChangedMessage(STATUS_NORMAL);
+ } catch (Exception e) {
+ e.printStackTrace();
+ sendStatusChangedMessage(STATUS_ERROR);
+ }
+ }
+ };
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ int ret = super.onStartCommand(intent, flags, startId);
+ if (BLOCKED_MAC_ADDRESS_FILE == null) {
+ BLOCKED_MAC_ADDRESS_FILE = new File(getDataDir(), "blocked.txt");
+ }
+ if (mHandler == null) {
+ mHandler = new Handler(getMainLooper()) {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ super.handleMessage(msg);
+ if (msg.what == MSG_STATUS_CHANGED) {
+ for (StatusListener listener : mListeners) {
+ listener.onStatusChanged(msg.arg1);
+ }
+ } else if (msg.what == MSG_UPDATE_CLIENT_LIST) {
+ if (mClientUpdateThread != null &&
+ mClientUpdateThread.getState() != Thread.State.NEW &&
+ mClientUpdateThread.getState() != Thread.State.TERMINATED) {
+ mHandler.removeMessages(MSG_UPDATE_CLIENT_LIST);
+ mHandler.sendMessageDelayed(msg, 1000);
+ return;
+ }
+ mClientUpdateThread = createClientUpdateThread((List<WifiClient>) msg.obj);
+ mClientUpdateThread.start();
+ }
+ }
+ };
+ }
+ if (mNetworkManagementService == null) {
+ mNetworkManagementService = INetworkManagementService.Stub.asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+ }
+ if (mWifiManager == null) {
+ mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ }
+ if (mBlockedMACList.isEmpty()) {
+ updateBlockedMACList();
+ }
+
+ if (!MACDataHelper.isInitialized()) {
+ MACDataHelper.init(this);
+ }
+
+ registerReceiver(mApStatusReceiver, new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION));
+
+ return ret;
+ }
+
+ @Override
+ public void onDestroy() {
+ unregisterReceiver(mApStatusReceiver);
+ mWifiManager.unregisterSoftApCallback(this);
+ super.onDestroy();
+ }
+
+ private void updateBlockedMACList() {
+ try {
+ mBlockedMACList.clear();
+ FileInputStream fis = new FileInputStream(BLOCKED_MAC_ADDRESS_FILE);
+ InputStreamReader inputStreamReader = new InputStreamReader(fis);
+ BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+ while (bufferedReader.ready()) {
+ String mac = bufferedReader.readLine();
+ blockMACAddress(mac);
+ }
+ bufferedReader.close();
+ inputStreamReader.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+
+ private void saveBlockedMACList() {
+ try {
+ BLOCKED_MAC_ADDRESS_FILE.createNewFile();
+ FileWriter fileWriter = new FileWriter(BLOCKED_MAC_ADDRESS_FILE);
+ for (String mac : mBlockedMACList) {
+ fileWriter.write(mac);
+ fileWriter.write('\n');
+ }
+ fileWriter.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private boolean blockMACAddress(String mac) {
+ if (!mBlockedMACList.contains(mac)) {
+ try {
+ mNetworkManagementService.setFirewallMACAddressRule(mac, false);
+ mBlockedMACList.add(mac);
+ if (mClients.containsKey(mac)) {
+ mClients.get(mac).setBlocked(true);
+ }
+ sendStatusChangedMessage(STATUS_BLOCK_LIST_UPDATED);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return false;
+ }
+
+ private boolean unblockMACAddress(String mac) {
+ if (mBlockedMACList.contains(mac)) {
+ try {
+ mNetworkManagementService.setFirewallMACAddressRule(mac, true);
+ mBlockedMACList.remove(mac);
+ if (mClients.containsKey(mac)) {
+ mClients.get(mac).setBlocked(false);
+ }
+ sendStatusChangedMessage(STATUS_BLOCK_LIST_UPDATED);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return false;
+ }
+
+ private void IUpdateClientList(List<WifiClient> clients) {
+
+ HashMap<String, ClientInfo> clientInfoHashMap = new HashMap<>();
+
+ sendStatusChangedMessage(STATUS_CLIENTS_REFRESHING);
+ // Get the currently connected device
+ for (WifiClient client : clients) {
+ String macAddress = client.getMacAddress().toString();
+ ClientInfo info = new ClientInfo(macAddress);
+ MACData macData = MACDataHelper.findMACData(macAddress);
+ if (macData != null) {
+ info.setManufacturer(macData.toString());
+ }
+ clientInfoHashMap.put(macAddress, info);
+ }
+ // Match MAC-IP
+ String res = execCommand(String.format(IP_NEIGHBOR_COMMAND_FORMAT, mInterfaceName));
+ if (!TextUtils.isEmpty(res)) {
+ String[] lines = res.split("\n");
+ for (String line : lines) {
+ String[] values = line.split(" ");
+ String ipAddress = values[COLUMN_IP_ADDRESS];
+ String macAddress = values[COLUMN_MAC_ADDRESS];
+ if (!isValidMACAddress(macAddress)) continue;
+ if (clientInfoHashMap.containsKey(macAddress)) {
+ ClientInfo info = clientInfoHashMap.get(macAddress);
+ info.addIPAddress(ipAddress);
+ }
+ }
+ }
+
+ // Get the list of blocked devices
+ for (String blockedMAC : mBlockedMACList) {
+ if (clientInfoHashMap.containsKey(blockedMAC)) {
+ clientInfoHashMap.get(blockedMAC).setBlocked(true);
+ } else {
+ ClientInfo info = new ClientInfo(blockedMAC);
+ MACData macData = MACDataHelper.findMACData(blockedMAC);
+ if (macData != null) {
+ info.setManufacturer(macData.toString());
+ }
+ info.setBlocked(true);
+ clientInfoHashMap.put(blockedMAC, info);
+ }
+ }
+
+ for (ClientInfo clientInfo : clientInfoHashMap.values()) {
+ getClientName(clientInfo);
+ }
+
+ mClients.clear();
+ mClients.putAll(clientInfoHashMap);
+
+ sendStatusChangedMessage(STATUS_CLIENTS_REFRESH_DONE);
+ }
+
+ private boolean isValidMACAddress(String mac) {
+ return mac != null && mac.matches("([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}");
+ }
+
+ private boolean isIPV4Address(String ip) {
+ return ip != null && ip.matches("((25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))");
+ }
+
+ private void sendStatusChangedMessage(int what) {
+ Message msg = mHandler.obtainMessage();
+ msg.what = MSG_STATUS_CHANGED;
+ msg.arg1 = what;
+ mHandler.sendMessage(msg);
+ }
+
+ private void getClientName(ClientInfo client) {
+ String[] ips = client.getIPAddressArray();
+ if (ips == null || ips.length == 0) return;
+ try {
+ boolean isIPV4 = isIPV4Address(ips[0]);
+ String[] ipStr;
+ if (isIPV4) {
+ ipStr = ips[0].split("\\.");
+ } else {
+ ipStr = ips[0].split(":");
+ }
+ byte[] ipBuffer = new byte[ipStr.length];
+ for(int i = 0; i < ipStr.length; i++){
+ ipBuffer[i] = (byte)(Integer.parseInt(ipStr[i], isIPV4 ? 10 : 16) & 0xff);
+ }
+ InetAddress inetAddress = InetAddress.getByAddress(ipBuffer);
+ client.setName(inetAddress.getHostName());
+ } catch (UnknownHostException e) {
+ client.setName(ips[0]);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new SoftApManageBinder();
+ }
+
+ @Override
+ public void onConnectedClientsChanged(List<WifiClient> clients) {
+ Message msg = mHandler.obtainMessage();
+ msg.what = MSG_UPDATE_CLIENT_LIST;
+ msg.obj = clients;
+ mHandler.sendMessage(msg);
+ }
+
+ private static String execCommand(String cmd) {
+ Runtime runtime = Runtime.getRuntime();
+ try {
+ Process process = runtime.exec(cmd);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ StringBuffer stringBuffer = new StringBuffer();
+ char[] buff = new char[1024];
+ int ch = 0;
+ while ((ch = reader.read(buff)) != -1) {
+ stringBuffer.append(buff, 0, ch);
+ }
+ reader.close();
+ process.waitFor();
+ if (process.exitValue() == 0) {
+ return stringBuffer.toString();
+ }
+ } catch (IOException | InterruptedException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public class SoftApManageBinder extends Binder {
+ public ClientInfo[] getClients() {
+ ClientInfo[] clientInfoArray = new ClientInfo[mClients.size()];
+ int i = 0;
+ for (ClientInfo clientInfo : mClients.values()) {
+ clientInfoArray[i++] = clientInfo.clone();
+ }
+ return clientInfoArray;
+ }
+
+ public ClientInfo getClientByMAC(String macAddress) {
+ ClientInfo client = mClients.get(macAddress);
+ if (client == null) {
+ return null;
+ } else {
+ return client.clone();
+ }
+ }
+
+ public boolean blockClient(String macAddress) {
+ if (blockMACAddress(macAddress)) {
+ saveBlockedMACList();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean unblockClient(String macAddress) {
+ if (unblockMACAddress(macAddress)) {
+ saveBlockedMACList();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean addStatusListener(StatusListener listener) {
+ if (mListeners.contains(listener)) return false;
+ return mListeners.add(listener);
+ }
+
+ public boolean removeStatusListener(StatusListener listener) {
+ return mListeners.remove(listener);
+ }
+ }
+
+ public interface StatusListener {
+ void onStatusChanged(int what);
+ }
+}