]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/frontends/android/app/src/main/java/org/strongswan/android/ui/LogFragment.java
f68d0c617e05a3b299cfa5939d20b7cc1fc67897
[thirdparty/strongswan.git] / src / frontends / android / app / src / main / java / org / strongswan / android / ui / LogFragment.java
1 /*
2 * Copyright (C) 2012-2017 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 package org.strongswan.android.ui;
17
18 import android.os.Bundle;
19 import android.os.FileObserver;
20 import android.os.Handler;
21 import android.support.v4.app.Fragment;
22 import android.view.LayoutInflater;
23 import android.view.View;
24 import android.view.ViewGroup;
25 import android.widget.TextView;
26
27 import org.strongswan.android.R;
28 import org.strongswan.android.logic.CharonVpnService;
29
30 import java.io.BufferedReader;
31 import java.io.File;
32 import java.io.FileNotFoundException;
33 import java.io.FileReader;
34 import java.io.StringReader;
35 import java.util.ArrayList;
36
37 public class LogFragment extends Fragment implements Runnable
38 {
39 private String mLogFilePath;
40 private Handler mLogHandler;
41 private TextView mLogView;
42 private LogScrollView mScrollView;
43 private BufferedReader mReader;
44 private Thread mThread;
45 private volatile boolean mRunning;
46 private FileObserver mDirectoryObserver;
47
48 @Override
49 public void onCreate(Bundle savedInstanceState)
50 {
51 super.onCreate(savedInstanceState);
52
53 mLogFilePath = getActivity().getFilesDir() + File.separator + CharonVpnService.LOG_FILE;
54 /* use a handler to update the log view */
55 mLogHandler = new Handler();
56
57 mDirectoryObserver = new LogDirectoryObserver(getActivity().getFilesDir().getAbsolutePath());
58 }
59
60 @Override
61 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
62 {
63 View view = inflater.inflate(R.layout.log_fragment, null);
64 mLogView = (TextView)view.findViewById(R.id.log_view);
65 mScrollView = (LogScrollView)view.findViewById(R.id.scroll_view);
66 return view;
67 }
68
69 @Override
70 public void onStart()
71 {
72 super.onStart();
73 startLogReader();
74 mDirectoryObserver.startWatching();
75 }
76
77 @Override
78 public void onStop()
79 {
80 super.onStop();
81 mDirectoryObserver.stopWatching();
82 stopLogReader();
83 }
84
85 /**
86 * Start reading from the log file
87 */
88 private void startLogReader()
89 {
90 try
91 {
92 mReader = new BufferedReader(new FileReader(mLogFilePath));
93 }
94 catch (FileNotFoundException e)
95 {
96 mReader = new BufferedReader(new StringReader(""));
97 }
98
99 mLogView.setText("");
100 mRunning = true;
101 mThread = new Thread(this);
102 mThread.start();
103 }
104
105 /**
106 * Stop reading from the log file
107 */
108 private void stopLogReader()
109 {
110 try
111 {
112 mRunning = false;
113 mThread.interrupt();
114 mThread.join();
115 }
116 catch (InterruptedException e)
117 {
118 }
119 }
120
121 /**
122 * Write the given log line to the TextView. We strip the prefix off to save
123 * some space (it is not that helpful for regular users anyway).
124 *
125 * @param lines log lines to log
126 */
127 public void logLines(final ArrayList<String> lines)
128 {
129 mLogHandler.post(new Runnable() {
130 @Override
131 public void run()
132 {
133 mLogView.beginBatchEdit();
134 for (String line : lines)
135 { /* strip off prefix (month=3, day=2, time=8, thread=2, spaces=3) */
136 mLogView.append((line.length() > 18 ? line.substring(18) : line) + '\n');
137 }
138 mLogView.endBatchEdit();
139 /* calling autoScroll() directly does not work, probably because content
140 * is not yet updated, so we post this to be done later */
141 mScrollView.post(new Runnable() {
142 @Override
143 public void run()
144 {
145 mScrollView.autoScroll();
146 }
147 });
148 }
149 });
150 }
151
152 @Override
153 public void run()
154 {
155 ArrayList<String> lines = null;
156
157 while (mRunning)
158 {
159 try
160 { /* this works as long as the file is not truncated */
161 String line = mReader.readLine();
162 if (line == null)
163 {
164 if (lines != null)
165 {
166 logLines(lines);
167 lines = null;
168 }
169 /* wait until there is more to log */
170 Thread.sleep(1000);
171 }
172 else
173 {
174 if (lines == null)
175 {
176 lines = new ArrayList<>();
177 }
178 lines.add(line);
179 }
180 }
181 catch (Exception e)
182 {
183 break;
184 }
185 }
186 if (lines != null)
187 {
188 logLines(lines);
189 }
190 }
191
192 /**
193 * FileObserver that checks for changes regarding the log file. Since charon
194 * truncates it (for which there is no explicit event) we check for any modification
195 * to the file, keep track of the file size and reopen it if it got smaller.
196 */
197 private class LogDirectoryObserver extends FileObserver
198 {
199 private final File mFile;
200 private long mSize;
201
202 public LogDirectoryObserver(String path)
203 {
204 super(path, FileObserver.CREATE | FileObserver.MODIFY | FileObserver.DELETE);
205 mFile = new File(mLogFilePath);
206 mSize = mFile.length();
207 }
208
209 @Override
210 public void onEvent(int event, String path)
211 {
212 if (path == null || !path.equals(CharonVpnService.LOG_FILE))
213 {
214 return;
215 }
216 switch (event)
217 { /* even though we only subscribed for these we check them,
218 * as strange events are sometimes received */
219 case FileObserver.CREATE:
220 case FileObserver.DELETE:
221 restartLogReader();
222 break;
223 case FileObserver.MODIFY:
224 /* if the size got smaller reopen the log file, as it was probably truncated */
225 long size = mFile.length();
226 if (size < mSize)
227 {
228 restartLogReader();
229 }
230 mSize = size;
231 break;
232 }
233 }
234
235 private void restartLogReader()
236 {
237 /* we are called from a separate thread, so we use the handler */
238 mLogHandler.post(new Runnable() {
239 @Override
240 public void run()
241 {
242 stopLogReader();
243 startLogReader();
244 }
245 });
246 }
247 }
248 }