/* SockStatUtil.java Copyright (c) 2015 NTT DOCOMO,INC. Released under the MIT license http://opensource.org/licenses/mit-license.php */ package org.deviceconnect.android.observer.util; import android.content.Context; import android.support.annotation.NonNull; import org.deviceconnect.android.manager.BuildConfig; import java.io.File; import java.util.ArrayList; import java.util.Scanner; import java.util.StringTokenizer; /** * Androidが使用しているSocketの状態を持つファイルを解析するユーティリティクラス. * * @author NTT DOCOMO, INC. * */ public final class SockStatUtil { /** * Sockstatファイルのローカルポート. */ private static final int LOCAL_IP_PORT = 1; /** * Sockstatファイルのリモートポート. */ private static final int REMOTE_IP_PORT = 2; /** * Sockstatファイルの接続状況. */ private static final int CONNECTION_STATE = 3; /** * SockstatファイルのUID. */ private static final int UID = 7; /** * 16進数. */ private static final int HEX = 16; /** * IPの16進数の文字列長. */ private static final int IP_HEX_LENGTH = 8; /** * IPの10進数の文字列長. */ private static final int IP_DECIMAL_LENGTH = 4; /** * コンストラクタ. */ private SockStatUtil() { } /** * SockstatファイルのSocketのリストを取得する. * @param context context * @return socket list */ public static synchronized ArrayList<AndroidSocket> getSocketList(final Context context) { ArrayList<AndroidSocket> allSocketList = new ArrayList<>(); if (context == null) { return allSocketList; } allSocketList.addAll(parseProcNetFile("/proc/net/tcp6", SocketType.TCP)); allSocketList.addAll(parseProcNetFile("/proc/net/tcp", SocketType.TCP)); allSocketList.addAll(parseProcNetFile("/proc/net/udp6", SocketType.UDP)); allSocketList.addAll(parseProcNetFile("/proc/net/udp", SocketType.UDP)); processAppInfo(context, allSocketList); return allSocketList; } /** * 指定されたSockstatファイルの解析を行う. * @param procFileName 解析するファイル名 * @param socketType 解析するSocketのタイプ * @return Socketリスト */ private static ArrayList<AndroidSocket> parseProcNetFile(final String procFileName, final SocketType socketType) { ArrayList<AndroidSocket> sockets = new ArrayList<>(); if (procFileName == null) { return sockets; } Scanner scanner = null; File file = new File(procFileName); try { scanner = new Scanner(file); scanner.nextLine(); while (scanner.hasNext()) { sockets.add(getAndroidSocket(socketType, scanner.nextLine())); } } catch (Exception e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } finally { if (scanner != null) { scanner.close(); } } return sockets; } @NonNull private static AndroidSocket getAndroidSocket(final SocketType socketType, final String nextLine) { AndroidSocket aSocket = new AndroidSocket(); aSocket.setType(socketType); StringTokenizer tokenizer = new StringTokenizer(nextLine); int tokenNo = 0; do { String token = tokenizer.nextToken(); switch (tokenNo) { case LOCAL_IP_PORT: // Analysis of Local IP and Port number String[] localAddress = token.split(":"); if (localAddress.length <= 1) { break; } aSocket.setLocalAddress(convertIPtoString(localAddress[0])); aSocket.setLocalPort(Integer.parseInt(localAddress[1], HEX)); break; case REMOTE_IP_PORT: // Analysis of Remote IP and Port number String[] remoteAddress = token.split(":"); if (remoteAddress.length <= 1) { break; } aSocket.setRemoteAddress(convertIPtoString(remoteAddress[0])); aSocket.setRemotePort(Integer.parseInt(remoteAddress[1], HEX)); break; case CONNECTION_STATE: //Connection status if (socketType == SocketType.TCP) { int l = Integer.parseInt(token, HEX); if (l > SocketState.values().length || l < 0) { l = 0; } aSocket.setState(SocketState.values()[l]); } else { aSocket.setState(SocketState.UDP_LISTEN); } break; case UID: //Uid of occupancy to the program aSocket.setUid(Integer.parseInt(token)); break; default: break; } tokenNo++; } while (tokenizer.hasMoreElements()); return aSocket; } /** * IP アドレスの解析. * @param ipString 16進数文字列のIPアドレス * @return IPv4のIPアドレス */ private static String convertIPtoString(final String ipString) { String s1 = ""; if (ipString == null || ipString.length() < IP_HEX_LENGTH) { return s1; } int i = ipString.length(); int j = 0; do { StringBuilder stringbuilder; String s2; stringbuilder = (new StringBuilder(String.valueOf(s1))) .append(Integer.parseInt(ipString.substring(i - 2, i), HEX)); if (j < IP_DECIMAL_LENGTH - 1) { s2 = "."; } else { s2 = ""; } s1 = stringbuilder.append(s2).toString(); i -= 2; } while (i >= 2 && ++j <= IP_DECIMAL_LENGTH); return s1; } /** * Socksocketの解析結果からアプリのパッケージ名を取得する. * @param context context * @param arrayList socketリスト */ private static void processAppInfo(final Context context, final ArrayList<AndroidSocket> arrayList) { if (arrayList == null) { return; } for (AndroidSocket androidsocket : arrayList) { String[] as = context.getPackageManager().getPackagesForUid(androidsocket.getUid()); if (as != null) { androidsocket.setAppName(as[0]); } else { androidsocket.setAppName("unknown"); } } } }