mirror of
https://github.com/schwabe/ics-openvpn.git
synced 2024-09-20 12:02:28 +02:00
Implement persistent disk log cache. (closes #340)
This commit is contained in:
parent
8414c4f167
commit
0e6e77c476
@ -37,5 +37,7 @@ public class ICSOpenVPNApplication extends Application {
|
|||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
//ACRA.init(this);
|
//ACRA.init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VpnStatus.initLogCache(getApplicationContext().getCacheDir());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
140
main/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java
Normal file
140
main/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2015 Arne Schwabe
|
||||||
|
* Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.blinkt.openvpn.core;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.Parcel;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by arne on 23.01.16.
|
||||||
|
*/
|
||||||
|
class LogFileHandler extends Handler {
|
||||||
|
static final int TRIM_LOG_FILE = 100;
|
||||||
|
static final int FLUSH_TO_DISK = 101;
|
||||||
|
static final int LOG_INIT = 102;
|
||||||
|
public static final int LOG_MESSAGE = 103;
|
||||||
|
private static FileOutputStream mLogFile;
|
||||||
|
private static BufferedOutputStream mBufLogfile;
|
||||||
|
|
||||||
|
public static final String LOGFILE_NAME = "logcache.dat";
|
||||||
|
|
||||||
|
|
||||||
|
public LogFileHandler(Looper looper) {
|
||||||
|
super(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
try {
|
||||||
|
if (msg.what == LOG_INIT) {
|
||||||
|
readLogCache((File) msg.obj);
|
||||||
|
openLogFile((File) msg.obj);
|
||||||
|
} else if (msg.what == LOG_MESSAGE && msg.obj instanceof VpnStatus.LogItem) {
|
||||||
|
// Ignore log messages if not yet initialized
|
||||||
|
if (mLogFile == null)
|
||||||
|
return;
|
||||||
|
writeLogItemToDisk((VpnStatus.LogItem) msg.obj);
|
||||||
|
} else if (msg.what == TRIM_LOG_FILE) {
|
||||||
|
trimLogFile();
|
||||||
|
for (VpnStatus.LogItem li : VpnStatus.getlogbuffer())
|
||||||
|
writeLogItemToDisk(li);
|
||||||
|
} else if (msg.what == FLUSH_TO_DISK) {
|
||||||
|
flushToDisk();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
VpnStatus.logError("Error during log cache: " + msg.what);
|
||||||
|
VpnStatus.logException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flushToDisk() throws IOException {
|
||||||
|
mLogFile.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void trimLogFile() {
|
||||||
|
try {
|
||||||
|
mBufLogfile.flush();
|
||||||
|
mLogFile.getChannel().truncate(0);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void writeLogItemToDisk(VpnStatus.LogItem li) throws IOException {
|
||||||
|
Parcel p = Parcel.obtain();
|
||||||
|
li.writeToParcel(p, 0);
|
||||||
|
// We do not really care if the log cache breaks between Android upgrades,
|
||||||
|
// write binary format to disc
|
||||||
|
byte[] liBytes = p.marshall();
|
||||||
|
|
||||||
|
mLogFile.write(liBytes.length & 0xff);
|
||||||
|
mLogFile.write(liBytes.length >> 8);
|
||||||
|
mLogFile.write(liBytes);
|
||||||
|
p.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openLogFile (File cacheDir) throws FileNotFoundException {
|
||||||
|
File logfile = new File(cacheDir, LOGFILE_NAME);
|
||||||
|
mLogFile = new FileOutputStream(logfile);
|
||||||
|
mBufLogfile = new BufferedOutputStream(mLogFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readLogCache(File cacheDir) {
|
||||||
|
File logfile = new File(cacheDir, LOGFILE_NAME);
|
||||||
|
|
||||||
|
if (!logfile.exists() || !logfile.canRead())
|
||||||
|
return;
|
||||||
|
|
||||||
|
VpnStatus.logDebug("Reread log items from cache file");
|
||||||
|
|
||||||
|
try {
|
||||||
|
BufferedInputStream logFile = new BufferedInputStream(new FileInputStream(logfile));
|
||||||
|
|
||||||
|
byte[] buf = new byte[8192];
|
||||||
|
int read = logFile.read(buf, 0, 2);
|
||||||
|
|
||||||
|
while (read > 0) {
|
||||||
|
// Marshalled LogItem
|
||||||
|
int len = (0xff & buf[0]) | buf[1] << 8;
|
||||||
|
|
||||||
|
read = logFile.read(buf, 0, len);
|
||||||
|
|
||||||
|
Parcel p = Parcel.obtain();
|
||||||
|
p.unmarshall(buf, 0, read);
|
||||||
|
p.setDataPosition(0);
|
||||||
|
VpnStatus.LogItem li = VpnStatus.LogItem.CREATOR.createFromParcel(p);
|
||||||
|
VpnStatus.newLogItem(li, true);
|
||||||
|
p.recycle();
|
||||||
|
|
||||||
|
//Next item
|
||||||
|
read = logFile.read(buf, 0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (java.io.IOException e) {
|
||||||
|
VpnStatus.logError("Reading cached logfile failed");
|
||||||
|
VpnStatus.logException(e);
|
||||||
|
e.printStackTrace();
|
||||||
|
// ignore reading file error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -493,6 +493,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
|
|||||||
}
|
}
|
||||||
// Just in case unregister for state
|
// Just in case unregister for state
|
||||||
VpnStatus.removeStateListener(this);
|
VpnStatus.removeStateListener(this);
|
||||||
|
VpnStatus.flushLog();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@ import android.content.pm.PackageManager;
|
|||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.pm.Signature;
|
import android.content.pm.Signature;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.HandlerThread;
|
||||||
|
import android.os.Message;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@ -112,6 +114,16 @@ public class VpnStatus {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void initLogCache(File cacheDir) {
|
||||||
|
Message m = mLogFileHandler.obtainMessage(LogFileHandler.LOG_INIT, cacheDir);
|
||||||
|
mLogFileHandler.sendMessage(m);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void flushLog() {
|
||||||
|
mLogFileHandler.sendEmptyMessage(LogFileHandler.FLUSH_TO_DISK);
|
||||||
|
}
|
||||||
|
|
||||||
public enum ConnectionStatus {
|
public enum ConnectionStatus {
|
||||||
LEVEL_CONNECTED,
|
LEVEL_CONNECTED,
|
||||||
LEVEL_VPNPAUSED,
|
LEVEL_VPNPAUSED,
|
||||||
@ -122,7 +134,7 @@ public class VpnStatus {
|
|||||||
LEVEL_START,
|
LEVEL_START,
|
||||||
LEVEL_AUTH_FAILED,
|
LEVEL_AUTH_FAILED,
|
||||||
LEVEL_WAITING_FOR_USER_INPUT,
|
LEVEL_WAITING_FOR_USER_INPUT,
|
||||||
UNKNOWN_LEVEL;
|
UNKNOWN_LEVEL
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum LogLevel {
|
public enum LogLevel {
|
||||||
@ -167,18 +179,24 @@ public class VpnStatus {
|
|||||||
|
|
||||||
private static ConnectionStatus mLastLevel = ConnectionStatus.LEVEL_NOTCONNECTED;
|
private static ConnectionStatus mLastLevel = ConnectionStatus.LEVEL_NOTCONNECTED;
|
||||||
|
|
||||||
|
private static final LogFileHandler mLogFileHandler;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
logbuffer = new LinkedList<>();
|
logbuffer = new LinkedList<>();
|
||||||
logListener = new Vector<>();
|
logListener = new Vector<>();
|
||||||
stateListener = new Vector<>();
|
stateListener = new Vector<>();
|
||||||
byteCountListener = new Vector<>();
|
byteCountListener = new Vector<>();
|
||||||
|
|
||||||
|
HandlerThread mHandlerThread = new HandlerThread("LogFileWriter", Thread.MIN_PRIORITY);
|
||||||
|
mHandlerThread.start();
|
||||||
|
mLogFileHandler = new LogFileHandler(mHandlerThread.getLooper());
|
||||||
|
|
||||||
logInformation();
|
logInformation();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class LogItem implements Parcelable {
|
public static class LogItem implements Parcelable {
|
||||||
|
|
||||||
|
|
||||||
private Object[] mArgs = null;
|
private Object[] mArgs = null;
|
||||||
private String mMessage = null;
|
private String mMessage = null;
|
||||||
private int mRessourceId;
|
private int mRessourceId;
|
||||||
@ -350,11 +368,6 @@ public class VpnStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveLogToDisk(Context c) {
|
|
||||||
|
|
||||||
File logOut = new File(c.getCacheDir(), "log.xml");
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface LogListener {
|
public interface LogListener {
|
||||||
void newLog(LogItem logItem);
|
void newLog(LogItem logItem);
|
||||||
}
|
}
|
||||||
@ -375,6 +388,7 @@ public class VpnStatus {
|
|||||||
public synchronized static void clearLog() {
|
public synchronized static void clearLog() {
|
||||||
logbuffer.clear();
|
logbuffer.clear();
|
||||||
logInformation();
|
logInformation();
|
||||||
|
mLogFileHandler.sendEmptyMessage(LogFileHandler.TRIM_LOG_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void logInformation() {
|
private static void logInformation() {
|
||||||
@ -409,32 +423,34 @@ public class VpnStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int getLocalizedState(String state) {
|
private static int getLocalizedState(String state) {
|
||||||
if (state.equals("CONNECTING"))
|
switch (state) {
|
||||||
|
case "CONNECTING":
|
||||||
return R.string.state_connecting;
|
return R.string.state_connecting;
|
||||||
else if (state.equals("WAIT"))
|
case "WAIT":
|
||||||
return R.string.state_wait;
|
return R.string.state_wait;
|
||||||
else if (state.equals("AUTH"))
|
case "AUTH":
|
||||||
return R.string.state_auth;
|
return R.string.state_auth;
|
||||||
else if (state.equals("GET_CONFIG"))
|
case "GET_CONFIG":
|
||||||
return R.string.state_get_config;
|
return R.string.state_get_config;
|
||||||
else if (state.equals("ASSIGN_IP"))
|
case "ASSIGN_IP":
|
||||||
return R.string.state_assign_ip;
|
return R.string.state_assign_ip;
|
||||||
else if (state.equals("ADD_ROUTES"))
|
case "ADD_ROUTES":
|
||||||
return R.string.state_add_routes;
|
return R.string.state_add_routes;
|
||||||
else if (state.equals("CONNECTED"))
|
case "CONNECTED":
|
||||||
return R.string.state_connected;
|
return R.string.state_connected;
|
||||||
else if (state.equals("DISCONNECTED"))
|
case "DISCONNECTED":
|
||||||
return R.string.state_disconnected;
|
return R.string.state_disconnected;
|
||||||
else if (state.equals("RECONNECTING"))
|
case "RECONNECTING":
|
||||||
return R.string.state_reconnecting;
|
return R.string.state_reconnecting;
|
||||||
else if (state.equals("EXITING"))
|
case "EXITING":
|
||||||
return R.string.state_exiting;
|
return R.string.state_exiting;
|
||||||
else if (state.equals("RESOLVE"))
|
case "RESOLVE":
|
||||||
return R.string.state_resolve;
|
return R.string.state_resolve;
|
||||||
else if (state.equals("TCP_CONNECT"))
|
case "TCP_CONNECT":
|
||||||
return R.string.state_tcp_connect;
|
return R.string.state_tcp_connect;
|
||||||
else
|
default:
|
||||||
return R.string.unknown_state;
|
return R.string.unknown_state;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,17 +552,33 @@ public class VpnStatus {
|
|||||||
newLogItem(new LogItem(LogLevel.DEBUG, resourceId, args));
|
newLogItem(new LogItem(LogLevel.DEBUG, resourceId, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void newLogItem(LogItem logItem) {
|
||||||
|
newLogItem(logItem, false);
|
||||||
|
}
|
||||||
|
|
||||||
private synchronized static void newLogItem(LogItem logItem) {
|
|
||||||
|
synchronized static void newLogItem(LogItem logItem, boolean cachedLine) {
|
||||||
|
if (cachedLine) {
|
||||||
|
logbuffer.addFirst(logItem);
|
||||||
|
} else {
|
||||||
logbuffer.addLast(logItem);
|
logbuffer.addLast(logItem);
|
||||||
if (logbuffer.size() > MAXLOGENTRIES)
|
Message m = mLogFileHandler.obtainMessage(LogFileHandler.LOG_MESSAGE, logItem);
|
||||||
|
mLogFileHandler.sendMessage(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logbuffer.size() > MAXLOGENTRIES + MAXLOGENTRIES / 2) {
|
||||||
|
while (logbuffer.size() > MAXLOGENTRIES)
|
||||||
logbuffer.removeFirst();
|
logbuffer.removeFirst();
|
||||||
|
mLogFileHandler.sendMessage(mLogFileHandler.obtainMessage(LogFileHandler.TRIM_LOG_FILE));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for (LogListener ll : logListener) {
|
for (LogListener ll : logListener) {
|
||||||
ll.newLog(logItem);
|
ll.newLog(logItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void logError(String msg) {
|
public static void logError(String msg) {
|
||||||
newLogItem(new LogItem(LogLevel.ERROR, msg));
|
newLogItem(new LogItem(LogLevel.ERROR, msg));
|
||||||
|
|
||||||
|
@ -39,5 +39,4 @@
|
|||||||
android:icon="@drawable/ic_menu_edit"
|
android:icon="@drawable/ic_menu_edit"
|
||||||
android:showAsAction="withText|ifRoom"
|
android:showAsAction="withText|ifRoom"
|
||||||
android:title="@string/edit_vpn"/>
|
android:title="@string/edit_vpn"/>
|
||||||
|
|
||||||
</menu>
|
</menu>
|
Loading…
Reference in New Issue
Block a user