mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-07-03 21:21:12 +08:00
Improve log viewer
- reduce logging level of firewall events to debug - only display the most recent 512 log lines
This commit is contained in:
parent
1d878c56c8
commit
4edd1e7ede
@ -1142,7 +1142,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
boolean lowMemory = (level != TRIM_MEMORY_UI_HIDDEN) && (level >= TRIM_MEMORY_RUNNING_LOW);
|
||||
boolean critical = lowMemory && (level >= TRIM_MEMORY_COMPLETE);
|
||||
|
||||
Log.w(TAG, "onTrimMemory: " + lvlStr + " - low=" + lowMemory + ", critical=" + critical);
|
||||
Log.d(TAG, "onTrimMemory: " + lvlStr + " - low=" + lowMemory + ", critical=" + critical);
|
||||
|
||||
if(critical && !mLowMemory)
|
||||
handleLowMemory();
|
||||
|
||||
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.emanuelef.remote_capture;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Reads lines in a file reversely (similar to a BufferedReader, but starting at
|
||||
* the last line). Useful for e.g. searching in log files.
|
||||
*
|
||||
* @since 2.2
|
||||
*/
|
||||
public class ReversedLinesFileReader implements Closeable {
|
||||
private final int blockSize;
|
||||
private final Charset encoding;
|
||||
private final RandomAccessFile randomAccessFile;
|
||||
private final long totalByteLength;
|
||||
private final long totalBlockCount;
|
||||
private final byte[][] newLineSequences;
|
||||
private final int avoidNewlineSplitBufferSize;
|
||||
private final int byteDecrement;
|
||||
private FilePart currentFilePart;
|
||||
private boolean trailingNewlineOfFileSkipped = false;
|
||||
/**
|
||||
* Creates a ReversedLinesFileReader with default block size of 4KB and the
|
||||
* platform's default encoding.
|
||||
*
|
||||
* @param file
|
||||
* the file to be read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @deprecated 2.5 use {@link #ReversedLinesFileReader(File, Charset)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public ReversedLinesFileReader(final File file) throws IOException {
|
||||
this(file, 4096, Charset.defaultCharset());
|
||||
}
|
||||
/**
|
||||
* Creates a ReversedLinesFileReader with default block size of 4KB and the
|
||||
* specified encoding.
|
||||
*
|
||||
* @param file
|
||||
* the file to be read
|
||||
* @param charset the encoding to use
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @since 2.5
|
||||
*/
|
||||
public ReversedLinesFileReader(final File file, final Charset charset) throws IOException {
|
||||
this(file, 4096, charset);
|
||||
}
|
||||
/**
|
||||
* Creates a ReversedLinesFileReader with the given block size and encoding.
|
||||
*
|
||||
* @param file
|
||||
* the file to be read
|
||||
* @param blockSize
|
||||
* size of the internal buffer (for ideal performance this should
|
||||
* match with the block size of the underlying file system).
|
||||
* @param encoding
|
||||
* the encoding of the file
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @since 2.3
|
||||
*/
|
||||
public ReversedLinesFileReader(final File file, final int blockSize, final Charset encoding) throws IOException {
|
||||
this.blockSize = blockSize;
|
||||
this.encoding = encoding;
|
||||
randomAccessFile = new RandomAccessFile(file, "r");
|
||||
totalByteLength = randomAccessFile.length();
|
||||
int lastBlockLength = (int) (totalByteLength % blockSize);
|
||||
if (lastBlockLength > 0) {
|
||||
totalBlockCount = totalByteLength / blockSize + 1;
|
||||
} else {
|
||||
totalBlockCount = totalByteLength / blockSize;
|
||||
if (totalByteLength > 0) {
|
||||
lastBlockLength = blockSize;
|
||||
}
|
||||
}
|
||||
currentFilePart = new FilePart(totalBlockCount, lastBlockLength, null);
|
||||
// --- check & prepare encoding ---
|
||||
final Charset charset = encoding;
|
||||
final CharsetEncoder charsetEncoder = charset.newEncoder();
|
||||
final float maxBytesPerChar = charsetEncoder.maxBytesPerChar();
|
||||
if (maxBytesPerChar == 1f) {
|
||||
// all one byte encodings are no problem
|
||||
byteDecrement = 1;
|
||||
} else if (charset == StandardCharsets.UTF_8) {
|
||||
// UTF-8 works fine out of the box, for multibyte sequences a second UTF-8 byte can never be a newline byte
|
||||
// http://en.wikipedia.org/wiki/UTF-8
|
||||
byteDecrement = 1;
|
||||
} else if (charset == Charset.forName("Shift_JIS")) {
|
||||
// Same as for UTF-8
|
||||
// http://www.herongyang.com/Unicode/JIS-Shift-JIS-Encoding.html
|
||||
byteDecrement = 1;
|
||||
} else if (charset == StandardCharsets.UTF_16BE || charset == StandardCharsets.UTF_16LE) {
|
||||
// UTF-16 new line sequences are not allowed as second tuple of four byte sequences,
|
||||
// however byte order has to be specified
|
||||
byteDecrement = 2;
|
||||
} else if (charset == StandardCharsets.UTF_16) {
|
||||
throw new UnsupportedEncodingException("For UTF-16, you need to specify the byte order (use UTF-16BE or UTF-16LE)");
|
||||
} else {
|
||||
throw new UnsupportedEncodingException("Encoding " + encoding + " is not supported yet (feel free to submit a patch)");
|
||||
}
|
||||
// NOTE: The new line sequences are matched in the order given, so it is important that \r\n is BEFORE \n
|
||||
newLineSequences = new byte[][] { "\r\n".getBytes(encoding), "\n".getBytes(encoding), "\r".getBytes(encoding) };
|
||||
avoidNewlineSplitBufferSize = newLineSequences[0].length;
|
||||
}
|
||||
/**
|
||||
* Returns the lines of the file from bottom to top.
|
||||
*
|
||||
* @return the next line or null if the start of the file is reached
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public String readLine() throws IOException {
|
||||
String line = currentFilePart.readLine();
|
||||
while (line == null) {
|
||||
currentFilePart = currentFilePart.rollOver();
|
||||
if (currentFilePart != null) {
|
||||
line = currentFilePart.readLine();
|
||||
} else {
|
||||
// no more fileparts: we're done, leave line set to null
|
||||
break;
|
||||
}
|
||||
}
|
||||
// aligned behaviour with BufferedReader that doesn't return a last, empty line
|
||||
if("".equals(line) && !trailingNewlineOfFileSkipped) {
|
||||
trailingNewlineOfFileSkipped = true;
|
||||
line = readLine();
|
||||
}
|
||||
return line;
|
||||
}
|
||||
/**
|
||||
* Closes underlying resources.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
randomAccessFile.close();
|
||||
}
|
||||
private class FilePart {
|
||||
private final long no;
|
||||
private final byte[] data;
|
||||
private byte[] leftOver;
|
||||
private int currentLastBytePos;
|
||||
/**
|
||||
* ctor
|
||||
* @param no the part number
|
||||
* @param length its length
|
||||
* @param leftOverOfLastFilePart remainder
|
||||
* @throws IOException if there is a problem reading the file
|
||||
*/
|
||||
private FilePart(final long no, final int length, final byte[] leftOverOfLastFilePart) throws IOException {
|
||||
this.no = no;
|
||||
final int dataLength = length + (leftOverOfLastFilePart != null ? leftOverOfLastFilePart.length : 0);
|
||||
this.data = new byte[dataLength];
|
||||
final long off = (no - 1) * blockSize;
|
||||
// read data
|
||||
if (no > 0 /* file not empty */) {
|
||||
randomAccessFile.seek(off);
|
||||
final int countRead = randomAccessFile.read(data, 0, length);
|
||||
if (countRead != length) {
|
||||
throw new IllegalStateException("Count of requested bytes and actually read bytes don't match");
|
||||
}
|
||||
}
|
||||
// copy left over part into data arr
|
||||
if (leftOverOfLastFilePart != null) {
|
||||
System.arraycopy(leftOverOfLastFilePart, 0, data, length, leftOverOfLastFilePart.length);
|
||||
}
|
||||
this.currentLastBytePos = data.length - 1;
|
||||
this.leftOver = null;
|
||||
}
|
||||
/**
|
||||
* Handles block rollover
|
||||
*
|
||||
* @return the new FilePart or null
|
||||
* @throws IOException if there was a problem reading the file
|
||||
*/
|
||||
private FilePart rollOver() throws IOException {
|
||||
if (currentLastBytePos > -1) {
|
||||
throw new IllegalStateException("Current currentLastCharPos unexpectedly positive... "
|
||||
+ "last readLine() should have returned something! currentLastCharPos=" + currentLastBytePos);
|
||||
}
|
||||
if (no > 1) {
|
||||
return new FilePart(no - 1, blockSize, leftOver);
|
||||
} else {
|
||||
// NO 1 was the last FilePart, we're finished
|
||||
if (leftOver != null) {
|
||||
throw new IllegalStateException("Unexpected leftover of the last block: leftOverOfThisFilePart="
|
||||
+ new String(leftOver, encoding));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Reads a line.
|
||||
*
|
||||
* @return the line or null
|
||||
*/
|
||||
private String readLine() {
|
||||
String line = null;
|
||||
int newLineMatchByteCount;
|
||||
final boolean isLastFilePart = no == 1;
|
||||
int i = currentLastBytePos;
|
||||
while (i > -1) {
|
||||
if (!isLastFilePart && i < avoidNewlineSplitBufferSize) {
|
||||
// avoidNewlineSplitBuffer: for all except the last file part we
|
||||
// take a few bytes to the next file part to avoid splitting of newlines
|
||||
createLeftOver();
|
||||
break; // skip last few bytes and leave it to the next file part
|
||||
}
|
||||
// --- check for newline ---
|
||||
if ((newLineMatchByteCount = getNewLineMatchByteCount(data, i)) > 0 /* found newline */) {
|
||||
final int lineStart = i + 1;
|
||||
final int lineLengthBytes = currentLastBytePos - lineStart + 1;
|
||||
if (lineLengthBytes < 0) {
|
||||
throw new IllegalStateException("Unexpected negative line length="+lineLengthBytes);
|
||||
}
|
||||
final byte[] lineData = new byte[lineLengthBytes];
|
||||
System.arraycopy(data, lineStart, lineData, 0, lineLengthBytes);
|
||||
line = new String(lineData, encoding);
|
||||
currentLastBytePos = i - newLineMatchByteCount;
|
||||
break; // found line
|
||||
}
|
||||
// --- move cursor ---
|
||||
i -= byteDecrement;
|
||||
// --- end of file part handling ---
|
||||
if (i < 0) {
|
||||
createLeftOver();
|
||||
break; // end of file part
|
||||
}
|
||||
}
|
||||
// --- last file part handling ---
|
||||
if (isLastFilePart && leftOver != null) {
|
||||
// there will be no line break anymore, this is the first line of the file
|
||||
line = new String(leftOver, encoding);
|
||||
leftOver = null;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
/**
|
||||
* Creates the buffer containing any left over bytes.
|
||||
*/
|
||||
private void createLeftOver() {
|
||||
final int lineLengthBytes = currentLastBytePos + 1;
|
||||
if (lineLengthBytes > 0) {
|
||||
// create left over for next block
|
||||
leftOver = new byte[lineLengthBytes];
|
||||
System.arraycopy(data, 0, leftOver, 0, lineLengthBytes);
|
||||
} else {
|
||||
leftOver = null;
|
||||
}
|
||||
currentLastBytePos = -1;
|
||||
}
|
||||
/**
|
||||
* Finds the new-line sequence and return its length.
|
||||
*
|
||||
* @param data buffer to scan
|
||||
* @param i start offset in buffer
|
||||
* @return length of newline sequence or 0 if none found
|
||||
*/
|
||||
private int getNewLineMatchByteCount(final byte[] data, final int i) {
|
||||
for (final byte[] newLineSequence : newLineSequences) {
|
||||
boolean match = true;
|
||||
for (int j = newLineSequence.length - 1; j >= 0; j--) {
|
||||
final int k = i + j - (newLineSequence.length - 1);
|
||||
match &= k >= 0 && data[k] == newLineSequence[j];
|
||||
}
|
||||
if (match) {
|
||||
return newLineSequence.length;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,13 +30,15 @@ import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.ReversedLinesFileReader;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class LogviewFragment extends Fragment {
|
||||
private static final String TAG = "LogviewFragment";
|
||||
public static final int MAX_LINES = 512;
|
||||
private String mLogPath;
|
||||
private String mLogText;
|
||||
private TextView mLogView;
|
||||
@ -68,14 +70,15 @@ public class LogviewFragment extends Fragment {
|
||||
|
||||
public void reloadLog() {
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(mLogPath));
|
||||
|
||||
ReversedLinesFileReader reader = new ReversedLinesFileReader(new File(mLogPath), StandardCharsets.US_ASCII);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
int count = 0;
|
||||
|
||||
while((line = reader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
builder.append("\n");
|
||||
while(((line = reader.readLine()) != null) && (count < MAX_LINES)) {
|
||||
builder.insert(0, "\n");
|
||||
builder.insert(0, line);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
mLogText = builder.toString();
|
||||
|
||||
@ -311,7 +311,7 @@ static void check_blacklisted_domain(pcapdroid_t *pd, pd_conn_t *data, const zdt
|
||||
char buf[512];
|
||||
|
||||
get_appname_by_uid(pd, data->uid, appbuf, sizeof(appbuf));
|
||||
log_i("Blocked domain [%s]: %s [%s]", data->info, zdtun_5tuple2str(tuple, buf, sizeof(buf)), appbuf);
|
||||
log_d("Blocked domain [%s]: %s [%s]", data->info, zdtun_5tuple2str(tuple, buf, sizeof(buf)), appbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -434,7 +434,7 @@ pd_conn_t* pd_new_connection(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, int u
|
||||
char buf[256];
|
||||
|
||||
get_appname_by_uid(pd, data->uid, appbuf, sizeof(appbuf));
|
||||
log_i("Blocked ip: %s [%s]", zdtun_5tuple2str(tuple, buf, sizeof(buf)), appbuf);
|
||||
log_d("Blocked ip: %s [%s]", zdtun_5tuple2str(tuple, buf, sizeof(buf)), appbuf);
|
||||
} else {
|
||||
data->to_block |= blacklist_match_uid(pd->firewall.bl, data->uid);
|
||||
if(data->to_block) {
|
||||
@ -442,7 +442,7 @@ pd_conn_t* pd_new_connection(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, int u
|
||||
char buf[256];
|
||||
|
||||
get_appname_by_uid(pd, data->uid, appbuf, sizeof(appbuf));
|
||||
log_i("Blocked app: %s [%s]", zdtun_5tuple2str(tuple, buf, sizeof(buf)), appbuf);
|
||||
log_d("Blocked app: %s [%s]", zdtun_5tuple2str(tuple, buf, sizeof(buf)), appbuf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user