mirror of https://github.com/M66B/NetGuard.git
Native log extra data (currently qnames only)
This commit is contained in:
parent
071124d3fc
commit
f061cb8f42
|
@ -15,7 +15,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
private static final String TAG = "NetGuard.Database";
|
||||
|
||||
private static final String DB_NAME = "Netguard";
|
||||
private static final int DB_VERSION = 7;
|
||||
private static final int DB_VERSION = 8;
|
||||
|
||||
private static List<LogChangedListener> logChangedListeners = new ArrayList<LogChangedListener>();
|
||||
|
||||
|
@ -44,13 +44,15 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
", daddr TEXT" +
|
||||
", dport INTEGER NULL" +
|
||||
", uid INTEGER NULL" +
|
||||
", data TEXT" +
|
||||
", allowed INTEGER NULL" +
|
||||
", connection INTEGER NULL" +
|
||||
", interactive INTEGER NULL" +
|
||||
");");
|
||||
db.execSQL("CREATE INDEX idx_log_time ON log(time)");
|
||||
db.execSQL("CREATE INDEX idx_log_source ON log(saddr, sport)");
|
||||
db.execSQL("CREATE INDEX idx_log_dest ON log(daddr, dport)");
|
||||
db.execSQL("CREATE INDEX idx_log_source ON log(saddr)");
|
||||
db.execSQL("CREATE INDEX idx_log_dest ON log(daddr)");
|
||||
db.execSQL("CREATE INDEX idx_log_uid ON log(uid)");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,6 +89,15 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
createTableLog(db);
|
||||
oldVersion = 7;
|
||||
}
|
||||
if (oldVersion < 8) {
|
||||
db.execSQL("ALTER TABLE log ADD COLUMN data TEXT");
|
||||
db.execSQL("DROP INDEX idx_log_source");
|
||||
db.execSQL("DROP INDEX idx_log_dest");
|
||||
db.execSQL("CREATE INDEX idx_log_source ON log(saddr)");
|
||||
db.execSQL("CREATE INDEX idx_log_dest ON log(daddr)");
|
||||
db.execSQL("CREATE INDEX idx_log_uid ON log(uid)");
|
||||
oldVersion = 8;
|
||||
}
|
||||
|
||||
db.setVersion(DB_VERSION);
|
||||
|
||||
|
@ -127,6 +138,8 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
else
|
||||
cv.put("dport", packet.dport);
|
||||
|
||||
cv.put("data", packet.data);
|
||||
|
||||
if (packet.uid < 0)
|
||||
cv.putNull("uid");
|
||||
else
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.database.Cursor;
|
|||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -37,6 +38,7 @@ public class LogAdapter extends CursorAdapter {
|
|||
private int colDest;
|
||||
private int colDPort;
|
||||
private int colUid;
|
||||
private int colData;
|
||||
private int colAllowed;
|
||||
private int colConnection;
|
||||
private int colInteractive;
|
||||
|
@ -56,6 +58,7 @@ public class LogAdapter extends CursorAdapter {
|
|||
colDest = cursor.getColumnIndex("daddr");
|
||||
colDPort = cursor.getColumnIndex("dport");
|
||||
colUid = cursor.getColumnIndex("uid");
|
||||
colData = cursor.getColumnIndex("data");
|
||||
colAllowed = cursor.getColumnIndex("allowed");
|
||||
colConnection = cursor.getColumnIndex("connection");
|
||||
colInteractive = cursor.getColumnIndex("interactive");
|
||||
|
@ -86,6 +89,7 @@ public class LogAdapter extends CursorAdapter {
|
|||
final String dest = cursor.getString(colDest);
|
||||
int dport = (cursor.isNull(colDPort) ? -1 : cursor.getInt(colDPort));
|
||||
int uid = (cursor.isNull(colUid) ? -1 : cursor.getInt(colUid));
|
||||
String data = cursor.getString(colData);
|
||||
int allowed = (cursor.isNull(colAllowed) ? -1 : cursor.getInt(colAllowed));
|
||||
int connection = (cursor.isNull(colConnection) ? -1 : cursor.getInt(colConnection));
|
||||
int interactive = (cursor.isNull(colInteractive) ? -1 : cursor.getInt(colInteractive));
|
||||
|
@ -100,6 +104,7 @@ public class LogAdapter extends CursorAdapter {
|
|||
TextView tvDPort = (TextView) view.findViewById(R.id.tvDPort);
|
||||
ImageView ivIcon = (ImageView) view.findViewById(R.id.ivIcon);
|
||||
TextView tvUid = (TextView) view.findViewById(R.id.tvUid);
|
||||
TextView tvData = (TextView) view.findViewById(R.id.tvData);
|
||||
ImageView ivConnection = (ImageView) view.findViewById(R.id.ivConnection);
|
||||
ImageView ivInteractive = (ImageView) view.findViewById(R.id.ivInteractive);
|
||||
|
||||
|
@ -199,6 +204,14 @@ public class LogAdapter extends CursorAdapter {
|
|||
}
|
||||
else
|
||||
tvDest.setText(getKnownAddress(dest));
|
||||
|
||||
if (TextUtils.isEmpty(data)) {
|
||||
tvData.setText("");
|
||||
tvData.setVisibility(View.GONE);
|
||||
} else {
|
||||
tvData.setText(data);
|
||||
tvData.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isKnownAddress(String addr) {
|
||||
|
|
|
@ -28,6 +28,7 @@ public class Packet {
|
|||
public int sport;
|
||||
public String daddr;
|
||||
public int dport;
|
||||
public String data;
|
||||
public int uid;
|
||||
public boolean allowed;
|
||||
|
||||
|
|
|
@ -1056,13 +1056,15 @@ void handle_ip(const struct arguments *args, const uint8_t *buffer, const size_t
|
|||
|
||||
// Handle allowed traffic
|
||||
int log = 0;
|
||||
char data[200];
|
||||
*data = 0;
|
||||
if (allowed) {
|
||||
if (protocol == IPPROTO_UDP) {
|
||||
allowed = handle_udp(args, buffer, length, uid);
|
||||
log = (debug || dport != 53);
|
||||
allowed = handle_udp(args, buffer, length, uid, data);
|
||||
log = (!allowed || dport != 53 || debug);
|
||||
} else if (protocol == IPPROTO_TCP) {
|
||||
allowed = handle_tcp(args, buffer, length, uid);
|
||||
log = (debug || syn);
|
||||
allowed = handle_tcp(args, buffer, length, uid, data);
|
||||
log = (!allowed || syn);
|
||||
}
|
||||
else {
|
||||
allowed = 0;
|
||||
|
@ -1072,10 +1074,12 @@ void handle_ip(const struct arguments *args, const uint8_t *buffer, const size_t
|
|||
|
||||
// Log traffic
|
||||
if (args->log && (!args->filter || log))
|
||||
log_packet(args, version, protocol, flags, source, sport, dest, dport, uid, allowed);
|
||||
log_packet(args, version, protocol, flags, source, sport, dest, dport, data, uid, allowed);
|
||||
}
|
||||
|
||||
jboolean handle_udp(const struct arguments *args, const uint8_t *buffer, size_t length, int uid) {
|
||||
jboolean handle_udp(const struct arguments *args,
|
||||
const uint8_t *buffer, size_t length,
|
||||
int uid, char *data) {
|
||||
// Check version
|
||||
uint8_t version = (*buffer) >> 4;
|
||||
|
||||
|
@ -1165,9 +1169,19 @@ jboolean handle_udp(const struct arguments *args, const uint8_t *buffer, size_t
|
|||
}
|
||||
|
||||
// Check for DNS
|
||||
if (ntohs(udphdr->dest) == 53 && check_dns(args, cur, buffer + dataoff, datalen)) {
|
||||
cur->stop = 1;
|
||||
return 0;
|
||||
if (ntohs(udphdr->dest) == 53) {
|
||||
char qname[DNS_QNAME_MAX + 1];
|
||||
uint16_t qtype;
|
||||
uint16_t qclass;
|
||||
if (get_dns(args, cur, buffer + dataoff, datalen, &qtype, &qclass, qname) >= 0) {
|
||||
log_android(ANDROID_LOG_INFO, "DNS type %d class %d name %s", qtype, qclass, qname);
|
||||
sprintf(data, "qtype %d qname %s", qtype, qname);
|
||||
|
||||
if (check_dns(args, cur, buffer + dataoff, datalen, qclass, qtype, qname)) {
|
||||
cur->stop = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO check DHCP (tethering)
|
||||
|
@ -1200,22 +1214,21 @@ jboolean handle_udp(const struct arguments *args, const uint8_t *buffer, size_t
|
|||
return 1;
|
||||
}
|
||||
|
||||
int check_dns(const struct arguments *args, const struct udp_session *u,
|
||||
const uint8_t *data, const size_t datalen) {
|
||||
int get_dns(const struct arguments *args, const struct udp_session *u,
|
||||
const uint8_t *data, const size_t datalen,
|
||||
uint16_t *qtype, uint16_t *qclass, char *name) {
|
||||
if (datalen > sizeof(struct dns_header)) {
|
||||
const struct dns_header *dns = (struct dns_header *) data;
|
||||
|
||||
// Check if standard DNS query
|
||||
// Wireshark: (udp.port eq 53)
|
||||
const struct dns_header *dns = (struct dns_header *) data;
|
||||
if (dns->qr == 0 && dns->opcode == 0 && dns->q_count != 0) {
|
||||
|
||||
char name[64];
|
||||
uint8_t noff = 0;
|
||||
|
||||
// http://tools.ietf.org/html/rfc1035
|
||||
uint8_t len;
|
||||
uint8_t comp = 0;
|
||||
size_t qdoff = sizeof(struct dns_header);
|
||||
do {
|
||||
comp++;
|
||||
len = *(data + qdoff);
|
||||
// TODO DNS compression
|
||||
if (len && qdoff + 1 + len <= datalen) {
|
||||
|
@ -1224,80 +1237,85 @@ int check_dns(const struct arguments *args, const struct udp_session *u,
|
|||
noff += (len + 1);
|
||||
qdoff += (1 + len);
|
||||
}
|
||||
} while (len);
|
||||
} while (len && comp < 10);
|
||||
qdoff++;
|
||||
|
||||
if (noff > 0 && qdoff + 4 == datalen) {
|
||||
*(name + noff - 1) = 0;
|
||||
uint16_t qtype = ntohs(*((uint16_t *) (data + qdoff)));
|
||||
uint16_t qclass = ntohs(*((uint16_t *) (data + qdoff + 2)));
|
||||
|
||||
log_android(ANDROID_LOG_INFO, "DNS type %d class %d name %s", qtype, qclass, name);
|
||||
|
||||
if (qclass == DNS_QCLASS_IN && (qtype == DNS_QTYPE_A || qtype == DNS_QTYPE_AAAA)) {
|
||||
for (int i = 0; i < args->hcount; i++)
|
||||
if (!strcmp(name, args->hosts[i])) {
|
||||
log_android(ANDROID_LOG_WARN, "DNS type %d class %d name %s blocked",
|
||||
qtype, qclass, name);
|
||||
|
||||
// Build response
|
||||
size_t rlen = datalen + sizeof(struct dns_rr) +
|
||||
(qtype == DNS_QTYPE_A ? 4 : 16);
|
||||
uint8_t *response = malloc(rlen);
|
||||
|
||||
// Copy header & query
|
||||
memcpy(response, data, datalen);
|
||||
|
||||
// Modify header
|
||||
struct dns_header *rh = (struct dns_header *) response;
|
||||
rh->qr = 1;
|
||||
rh->aa = 0;
|
||||
rh->tc = 0;
|
||||
rh->rd = 0;
|
||||
rh->ra = 0;
|
||||
rh->z = 0;
|
||||
rh->ad = 0;
|
||||
rh->cd = 0;
|
||||
rh->rcode = 0;
|
||||
rh->ans_count = htons(1);
|
||||
rh->auth_count = 0;
|
||||
rh->add_count = 0;
|
||||
|
||||
// Build answer
|
||||
struct dns_rr *answer = (struct dns_rr *) (response + datalen);
|
||||
answer->qname_ptr = htons(sizeof(struct dns_header) | 0xC000);
|
||||
answer->qtype = htons(qtype);
|
||||
answer->qclass = htons(qclass);
|
||||
answer->ttl = htonl(DNS_TTL);
|
||||
answer->rdlength = htons(qtype == DNS_QTYPE_A ? 4 : 16);
|
||||
|
||||
// Add answer address
|
||||
uint8_t *addr = response + datalen + sizeof(struct dns_rr);
|
||||
if (qtype == DNS_QTYPE_A)
|
||||
inet_pton(AF_INET, "127.0.0.1", addr);
|
||||
else
|
||||
inet_pton(AF_INET6, "::1", addr);
|
||||
|
||||
// Send response
|
||||
ssize_t res = write_udp(args, u, response, rlen);
|
||||
|
||||
free(response);
|
||||
|
||||
if (res < 0)
|
||||
log_android(ANDROID_LOG_ERROR, "write UDP error %d: %s",
|
||||
errno, strerror((errno)));
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
*qtype = ntohs(*((uint16_t *) (data + qdoff)));
|
||||
*qclass = ntohs(*((uint16_t *) (data + qdoff + 2)));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int check_dns(const struct arguments *args, const struct udp_session *u,
|
||||
const uint8_t *data, const size_t datalen,
|
||||
uint16_t qclass, uint16_t qtype, const char *name) {
|
||||
if (qclass == DNS_QCLASS_IN && (qtype == DNS_QTYPE_A || qtype == DNS_QTYPE_AAAA)) {
|
||||
for (int i = 0; i < args->hcount; i++)
|
||||
if (!strcmp(name, args->hosts[i])) {
|
||||
log_android(ANDROID_LOG_WARN, "DNS type %d name %s blocked", qtype, name);
|
||||
|
||||
// Build response
|
||||
size_t rlen = datalen + sizeof(struct dns_rr) + (qtype == DNS_QTYPE_A ? 4 : 16);
|
||||
uint8_t *response = malloc(rlen);
|
||||
|
||||
// Copy header & query
|
||||
memcpy(response, data, datalen);
|
||||
|
||||
// Modify copied header
|
||||
struct dns_header *rh = (struct dns_header *) response;
|
||||
rh->qr = 1;
|
||||
rh->aa = 0;
|
||||
rh->tc = 0;
|
||||
rh->rd = 0;
|
||||
rh->ra = 0;
|
||||
rh->z = 0;
|
||||
rh->ad = 0;
|
||||
rh->cd = 0;
|
||||
rh->rcode = 0;
|
||||
rh->ans_count = htons(1);
|
||||
rh->auth_count = 0;
|
||||
rh->add_count = 0;
|
||||
|
||||
// Build answer
|
||||
struct dns_rr *answer = (struct dns_rr *) (response + datalen);
|
||||
answer->qname_ptr = htons(sizeof(struct dns_header) | 0xC000);
|
||||
answer->qtype = htons(qtype);
|
||||
answer->qclass = htons(qclass);
|
||||
answer->ttl = htonl(DNS_TTL);
|
||||
answer->rdlength = htons(qtype == DNS_QTYPE_A ? 4 : 16);
|
||||
|
||||
// Add answer address
|
||||
uint8_t *addr = response + datalen + sizeof(struct dns_rr);
|
||||
if (qtype == DNS_QTYPE_A)
|
||||
inet_pton(AF_INET, "127.0.0.1", addr);
|
||||
else
|
||||
inet_pton(AF_INET6, "::1", addr);
|
||||
|
||||
// Send response
|
||||
ssize_t res = write_udp(args, u, response, rlen);
|
||||
|
||||
free(response);
|
||||
|
||||
if (res < 0)
|
||||
log_android(ANDROID_LOG_ERROR, "write UDP error %d: %s",
|
||||
errno, strerror((errno)));
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
jboolean handle_tcp(const struct arguments *args, const uint8_t *buffer, size_t length, int uid) {
|
||||
jboolean handle_tcp(const struct arguments *args,
|
||||
const uint8_t *buffer, size_t length,
|
||||
int uid, char *data) {
|
||||
#ifdef PROFILE
|
||||
float mselapsed;
|
||||
struct timeval start, end;
|
||||
|
@ -1926,7 +1944,7 @@ ssize_t write_udp(const struct arguments *args, const struct udp_session *cur,
|
|||
if (res >= 0) {
|
||||
if (args->log && (debug || ntohs(cur->dest) != 53))
|
||||
log_packet(args, cur->version, IPPROTO_UDP, "",
|
||||
source, ntohs(udp->source), dest, ntohs(udp->dest), cur->uid, 1);
|
||||
source, ntohs(udp->source), dest, ntohs(udp->dest), "", cur->uid, 1);
|
||||
|
||||
// Write pcap record
|
||||
if (pcap_file != NULL)
|
||||
|
@ -2323,6 +2341,7 @@ void log_packet(
|
|||
jint sport,
|
||||
const char *dest,
|
||||
jint dport,
|
||||
const char *data,
|
||||
jint uid,
|
||||
jboolean allowed) {
|
||||
#ifdef PROFILE
|
||||
|
@ -2347,6 +2366,7 @@ void log_packet(
|
|||
jstring jflags = (*env)->NewStringUTF(env, flags);
|
||||
jstring jsource = (*env)->NewStringUTF(env, source);
|
||||
jstring jdest = (*env)->NewStringUTF(env, dest);
|
||||
jstring jdata = (*env)->NewStringUTF(env, data);
|
||||
|
||||
const char *string = "Ljava/lang/String;";
|
||||
(*env)->SetLongField(env, objPacket, jniGetFieldID(env, clsPacket, "time", "J"), t);
|
||||
|
@ -2357,12 +2377,14 @@ void log_packet(
|
|||
(*env)->SetIntField(env, objPacket, jniGetFieldID(env, clsPacket, "sport", "I"), sport);
|
||||
(*env)->SetObjectField(env, objPacket, jniGetFieldID(env, clsPacket, "daddr", string), jdest);
|
||||
(*env)->SetIntField(env, objPacket, jniGetFieldID(env, clsPacket, "dport", "I"), dport);
|
||||
(*env)->SetObjectField(env, objPacket, jniGetFieldID(env, clsPacket, "data", string), jdata);
|
||||
(*env)->SetIntField(env, objPacket, jniGetFieldID(env, clsPacket, "uid", "I"), uid);
|
||||
(*env)->SetBooleanField(env, objPacket, jniGetFieldID(env, clsPacket, "allowed", "Z"), allowed);
|
||||
|
||||
(*env)->CallVoidMethod(env, args->instance, logPacket, objPacket);
|
||||
jniCheckException(env);
|
||||
|
||||
(*env)->DeleteLocalRef(env, jdata);
|
||||
(*env)->DeleteLocalRef(env, jdest);
|
||||
(*env)->DeleteLocalRef(env, jsource);
|
||||
(*env)->DeleteLocalRef(env, jflags);
|
||||
|
|
|
@ -51,10 +51,11 @@ struct udp_session {
|
|||
__be32 ip4; // network notation
|
||||
struct in6_addr ip6;
|
||||
} daddr;
|
||||
|
||||
__be16 dest; // network notation
|
||||
|
||||
uint8_t stop;
|
||||
jint socket;
|
||||
|
||||
struct udp_session *next;
|
||||
};
|
||||
|
||||
|
@ -83,9 +84,11 @@ struct tcp_session {
|
|||
|
||||
uint8_t state;
|
||||
jint socket;
|
||||
|
||||
struct tcp_session *next;
|
||||
};
|
||||
|
||||
// PCAP
|
||||
// https://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||
|
||||
typedef uint16_t guint16_t;
|
||||
|
@ -111,6 +114,15 @@ typedef struct pcaprec_hdr_s {
|
|||
|
||||
#define LINKTYPE_RAW 101
|
||||
|
||||
// DNS
|
||||
|
||||
#define DNS_QTYPE_A 1 // IPv4
|
||||
#define DNS_QTYPE_AAAA 28 // IPv6
|
||||
#define DNS_QCLASS_IN 1
|
||||
|
||||
#define DNS_QNAME_MAX 63
|
||||
#define DNS_TTL (10 * 60) // seconds
|
||||
|
||||
struct dns_header {
|
||||
uint16_t id; // identification number
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
|
@ -152,11 +164,7 @@ typedef struct dns_rr {
|
|||
__be16 rdlength;
|
||||
} __packed;
|
||||
|
||||
#define DNS_QTYPE_A 1 // IPv4
|
||||
#define DNS_QTYPE_AAAA 28 // IPv6
|
||||
#define DNS_QCLASS_IN 1
|
||||
|
||||
#define DNS_TTL (10 * 60) // seconds
|
||||
// IPv6
|
||||
|
||||
struct ip6_hdr_pseudo {
|
||||
struct in6_addr ip6ph_src;
|
||||
|
@ -166,6 +174,8 @@ struct ip6_hdr_pseudo {
|
|||
u_int8_t ip6ph_nxt;
|
||||
} __packed;
|
||||
|
||||
// Prototypes
|
||||
|
||||
void check_allowed(const struct arguments *args);
|
||||
|
||||
void clear_sessions();
|
||||
|
@ -188,12 +198,21 @@ void check_tcp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds,
|
|||
|
||||
void handle_ip(const struct arguments *args, const uint8_t *buffer, size_t length);
|
||||
|
||||
jboolean handle_udp(const struct arguments *args, const uint8_t *buffer, size_t length, int uid);
|
||||
jboolean handle_udp(const struct arguments *args,
|
||||
const uint8_t *buffer, size_t length,
|
||||
int uid, char *data);
|
||||
|
||||
jboolean handle_tcp(const struct arguments *args, const uint8_t *buffer, size_t length, int uid);
|
||||
jboolean handle_tcp(const struct arguments *args,
|
||||
const uint8_t *buffer, size_t length,
|
||||
int uid, char *data);
|
||||
|
||||
int get_dns(const struct arguments *args, const struct udp_session *u,
|
||||
const uint8_t *data, const size_t datalen,
|
||||
uint16_t *qtype, uint16_t *qclass, char *name);
|
||||
|
||||
int check_dns(const struct arguments *args, const struct udp_session *u,
|
||||
const uint8_t *data, const size_t datalen);
|
||||
const uint8_t *data, const size_t datalen,
|
||||
uint16_t qclass, uint16_t qtype, const char *name);
|
||||
|
||||
int open_udp_socket(const struct arguments *args, const struct udp_session *cur);
|
||||
|
||||
|
@ -254,6 +273,7 @@ void log_packet(const struct arguments *args,
|
|||
jint sport,
|
||||
const char *dest,
|
||||
jint dport,
|
||||
const char *data,
|
||||
jint uid,
|
||||
jboolean allowed);
|
||||
|
||||
|
|
|
@ -3,119 +3,133 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ActivityLog">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTime"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivIcon"
|
||||
android:layout_width="24dip"
|
||||
android:layout_height="24dip"
|
||||
android:layout_gravity="center_vertical" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvUid"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="end"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivConnection"
|
||||
android:layout_width="16dip"
|
||||
android:layout_height="16dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivInteractive"
|
||||
android:layout_width="16dip"
|
||||
android:layout_height="16dip"
|
||||
android:layout_gravity="center_vertical" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:orientation="vertical">
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvProtocol"
|
||||
android:layout_width="40dp"
|
||||
android:id="@+id/tvTime"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivIcon"
|
||||
android:layout_width="24dip"
|
||||
android:layout_height="24dip"
|
||||
android:layout_gravity="center_vertical" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvFlags"
|
||||
android:layout_width="40dp"
|
||||
android:id="@+id/tvUid"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="end"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivConnection"
|
||||
android:layout_width="16dip"
|
||||
android:layout_height="16dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivInteractive"
|
||||
android:layout_width="16dip"
|
||||
android:layout_height="16dip"
|
||||
android:layout_gravity="center_vertical" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvProtocol"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvFlags"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSPort"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSource"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDPort"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDest"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
<TextView
|
||||
android:id="@+id/tvData"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSPort"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSource"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDPort"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDest"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
android:layout_marginStart="50dp"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Small"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
|
Loading…
Reference in New Issue