Watch for changes to the log file so we can reopen it
authorTobias Brunner <tobias@strongswan.org>
Fri, 10 Aug 2012 08:05:38 +0000 (10:05 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 13 Aug 2012 09:22:21 +0000 (11:22 +0200)
If the log fragment is shown while the daemon starts (which is not the
case at the moment, but maybe later on tablets) the file reader would not
notice that the file got truncated.  The same applies if the file is deleted
directly on the file system e.g. with adb shell.

src/frontends/android/src/org/strongswan/android/ui/LogFragment.java

index 23cd2aa..8740e0c 100644 (file)
@@ -26,6 +26,7 @@ import org.strongswan.android.logic.CharonVpnService;
 
 import android.app.Fragment;
 import android.os.Bundle;
+import android.os.FileObserver;
 import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -41,6 +42,7 @@ public class LogFragment extends Fragment implements Runnable
        private BufferedReader mReader;
        private Thread mThread;
        private volatile boolean mRunning;
+       private FileObserver mDirectoryObserver;
 
        @Override
        public void onCreate(Bundle savedInstanceState)
@@ -50,6 +52,8 @@ public class LogFragment extends Fragment implements Runnable
                mLogFilePath = getActivity().getFilesDir() + File.separator + CharonVpnService.LOG_FILE;
                /* use a handler to update the log view */
                mLogHandler = new Handler();
+
+               mDirectoryObserver = new LogDirectoryObserver(getActivity().getFilesDir().getAbsolutePath());
        }
 
        @Override
@@ -65,7 +69,23 @@ public class LogFragment extends Fragment implements Runnable
        public void onStart()
        {
                super.onStart();
-               mLogView.setText("");
+               startLogReader();
+               mDirectoryObserver.startWatching();
+       }
+
+       @Override
+       public void onStop()
+       {
+               super.onStop();
+               mDirectoryObserver.stopWatching();
+               stopLogReader();
+       }
+
+       /**
+        * Start reading from the log file
+        */
+       private void startLogReader()
+       {
                try
                {
                        mReader = new BufferedReader(new FileReader(mLogFilePath));
@@ -74,15 +94,18 @@ public class LogFragment extends Fragment implements Runnable
                {
                        mReader = new BufferedReader(new StringReader(""));
                }
+
+               mLogView.setText("");
                mRunning = true;
                mThread = new Thread(this);
                mThread.start();
        }
 
-       @Override
-       public void onStop()
+       /**
+        * Stop reading from the log file
+        */
+       private void stopLogReader()
        {
-               super.onStop();
                try
                {
                        mRunning = false;
@@ -97,6 +120,7 @@ public class LogFragment extends Fragment implements Runnable
        /**
         * Write the given log line to the TextView. We strip the prefix off to save
         * some space (it is not that helpful for regular users anyway).
+        *
         * @param line log line to log
         */
        public void logLine(final String line)
@@ -126,7 +150,7 @@ public class LogFragment extends Fragment implements Runnable
                while (mRunning)
                {
                        try
-                       {
+                       {       /* this works as long as the file is not truncated */
                                String line = mReader.readLine();
                                if (line == null)
                                {       /* wait until there is more to log */
@@ -143,4 +167,61 @@ public class LogFragment extends Fragment implements Runnable
                        }
                }
        }
+
+       /**
+        * FileObserver that checks for changes regarding the log file. Since charon
+        * truncates it (for which there is no explicit event) we check for any modification
+        * to the file, keep track of the file size and reopen it if it got smaller.
+        */
+       private class LogDirectoryObserver extends FileObserver
+       {
+               private final File mFile;
+               private long mSize;
+
+               public LogDirectoryObserver(String path)
+               {
+                       super(path, FileObserver.CREATE | FileObserver.MODIFY | FileObserver.DELETE);
+                       mFile = new File(mLogFilePath);
+                       mSize = mFile.length();
+               }
+
+               @Override
+               public void onEvent(int event, String path)
+               {
+                       if (path == null || !path.equals(CharonVpnService.LOG_FILE))
+                       {
+                               return;
+                       }
+                       switch (event)
+                       {       /* even though we only subscribed for these we check them,
+                                * as strange events are sometimes received */
+                               case FileObserver.CREATE:
+                               case FileObserver.DELETE:
+                                       restartLogReader();
+                                       break;
+                               case FileObserver.MODIFY:
+                                       /* if the size got smaller reopen the log file, as it was probably truncated */
+                                       long size = mFile.length();
+                                       if (size < mSize)
+                                       {
+                                               restartLogReader();
+                                       }
+                                       mSize = size;
+                                       break;
+                       }
+               }
+
+               private void restartLogReader()
+               {
+                       /* we are called from a separate thread, so we use the handler */
+                       mLogHandler.post(new Runnable() {
+                               @Override
+                               public void run()
+                               {
+                                       stopLogReader();
+                                       startLogReader();
+                               }
+                       });
+               }
+       }
 }