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) {
|
||||
//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
|
||||
VpnStatus.removeStateListener(this);
|
||||
VpnStatus.flushLog();
|
||||
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,8 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Build;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
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 {
|
||||
LEVEL_CONNECTED,
|
||||
LEVEL_VPNPAUSED,
|
||||
@ -122,7 +134,7 @@ public class VpnStatus {
|
||||
LEVEL_START,
|
||||
LEVEL_AUTH_FAILED,
|
||||
LEVEL_WAITING_FOR_USER_INPUT,
|
||||
UNKNOWN_LEVEL;
|
||||
UNKNOWN_LEVEL
|
||||
}
|
||||
|
||||
public enum LogLevel {
|
||||
@ -167,18 +179,24 @@ public class VpnStatus {
|
||||
|
||||
private static ConnectionStatus mLastLevel = ConnectionStatus.LEVEL_NOTCONNECTED;
|
||||
|
||||
private static final LogFileHandler mLogFileHandler;
|
||||
|
||||
static {
|
||||
logbuffer = new LinkedList<>();
|
||||
logListener = new Vector<>();
|
||||
stateListener = new Vector<>();
|
||||
byteCountListener = new Vector<>();
|
||||
|
||||
HandlerThread mHandlerThread = new HandlerThread("LogFileWriter", Thread.MIN_PRIORITY);
|
||||
mHandlerThread.start();
|
||||
mLogFileHandler = new LogFileHandler(mHandlerThread.getLooper());
|
||||
|
||||
logInformation();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class LogItem implements Parcelable {
|
||||
|
||||
|
||||
private Object[] mArgs = null;
|
||||
private String mMessage = null;
|
||||
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 {
|
||||
void newLog(LogItem logItem);
|
||||
}
|
||||
@ -375,6 +388,7 @@ public class VpnStatus {
|
||||
public synchronized static void clearLog() {
|
||||
logbuffer.clear();
|
||||
logInformation();
|
||||
mLogFileHandler.sendEmptyMessage(LogFileHandler.TRIM_LOG_FILE);
|
||||
}
|
||||
|
||||
private static void logInformation() {
|
||||
@ -409,32 +423,34 @@ public class VpnStatus {
|
||||
}
|
||||
|
||||
private static int getLocalizedState(String state) {
|
||||
if (state.equals("CONNECTING"))
|
||||
switch (state) {
|
||||
case "CONNECTING":
|
||||
return R.string.state_connecting;
|
||||
else if (state.equals("WAIT"))
|
||||
case "WAIT":
|
||||
return R.string.state_wait;
|
||||
else if (state.equals("AUTH"))
|
||||
case "AUTH":
|
||||
return R.string.state_auth;
|
||||
else if (state.equals("GET_CONFIG"))
|
||||
case "GET_CONFIG":
|
||||
return R.string.state_get_config;
|
||||
else if (state.equals("ASSIGN_IP"))
|
||||
case "ASSIGN_IP":
|
||||
return R.string.state_assign_ip;
|
||||
else if (state.equals("ADD_ROUTES"))
|
||||
case "ADD_ROUTES":
|
||||
return R.string.state_add_routes;
|
||||
else if (state.equals("CONNECTED"))
|
||||
case "CONNECTED":
|
||||
return R.string.state_connected;
|
||||
else if (state.equals("DISCONNECTED"))
|
||||
case "DISCONNECTED":
|
||||
return R.string.state_disconnected;
|
||||
else if (state.equals("RECONNECTING"))
|
||||
case "RECONNECTING":
|
||||
return R.string.state_reconnecting;
|
||||
else if (state.equals("EXITING"))
|
||||
case "EXITING":
|
||||
return R.string.state_exiting;
|
||||
else if (state.equals("RESOLVE"))
|
||||
case "RESOLVE":
|
||||
return R.string.state_resolve;
|
||||
else if (state.equals("TCP_CONNECT"))
|
||||
case "TCP_CONNECT":
|
||||
return R.string.state_tcp_connect;
|
||||
else
|
||||
default:
|
||||
return R.string.unknown_state;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -536,17 +552,33 @@ public class VpnStatus {
|
||||
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);
|
||||
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();
|
||||
mLogFileHandler.sendMessage(mLogFileHandler.obtainMessage(LogFileHandler.TRIM_LOG_FILE));
|
||||
}
|
||||
|
||||
|
||||
for (LogListener ll : logListener) {
|
||||
ll.newLog(logItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void logError(String msg) {
|
||||
newLogItem(new LogItem(LogLevel.ERROR, msg));
|
||||
|
||||
|
@ -39,5 +39,4 @@
|
||||
android:icon="@drawable/ic_menu_edit"
|
||||
android:showAsAction="withText|ifRoom"
|
||||
android:title="@string/edit_vpn"/>
|
||||
|
||||
</menu>
|
Loading…
Reference in New Issue
Block a user