]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/frontends/android/app/src/main/java/org/strongswan/android/ui/LogFragment.java
nm: Don't set DL_LIBS to 'none required' in configure script
[thirdparty/strongswan.git] / src / frontends / android / app / src / main / java / org / strongswan / android / ui / LogFragment.java
CommitLineData
f9a162a2 1/*
9e05f219 2 * Copyright (C) 2012-2018 Tobias Brunner
19ef2aec
TB
3 *
4 * Copyright (C) secunet Security Networks AG
f9a162a2
TB
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17package org.strongswan.android.ui;
18
9e05f219 19import android.content.Context;
c9761655 20import android.os.Build;
f9a162a2 21import android.os.Bundle;
ae10e8c4 22import android.os.FileObserver;
f9a162a2 23import android.os.Handler;
dc351a30 24import android.os.Looper;
f9a162a2
TB
25import android.view.LayoutInflater;
26import android.view.View;
27import android.view.ViewGroup;
9e05f219
TB
28import android.widget.ArrayAdapter;
29import android.widget.ListView;
f9a162a2 30
7c5fec3a
TB
31import org.strongswan.android.R;
32import org.strongswan.android.logic.CharonVpnService;
33
34import java.io.BufferedReader;
35import java.io.File;
36import java.io.FileNotFoundException;
37import java.io.FileReader;
38import java.io.StringReader;
74d44e15 39import java.util.ArrayList;
7c5fec3a 40
3b9696fc 41import androidx.annotation.NonNull;
c9761655 42import androidx.annotation.RequiresApi;
3b9696fc
TB
43import androidx.fragment.app.Fragment;
44
9e05f219 45public class LogFragment extends Fragment
f9a162a2 46{
9e05f219 47 private static String SCROLL_POSITION = "SCROLL_POSITION";
f9a162a2
TB
48 private String mLogFilePath;
49 private Handler mLogHandler;
9e05f219
TB
50 private ListView mLog;
51 private LogAdapter mLogAdapter;
ae10e8c4 52 private FileObserver mDirectoryObserver;
9e05f219 53 private int mScrollPosition;
f9a162a2
TB
54
55 @Override
56 public void onCreate(Bundle savedInstanceState)
57 {
58 super.onCreate(savedInstanceState);
59
60 mLogFilePath = getActivity().getFilesDir() + File.separator + CharonVpnService.LOG_FILE;
9e05f219 61
dc351a30 62 mLogHandler = new Handler(Looper.getMainLooper());
ae10e8c4 63
c9761655
TB
64 File logdir = getActivity().getFilesDir();
65 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
66 {
67 mDirectoryObserver = new LogDirectoryObserver(logdir);
68 }
69 else
70 {
71 mDirectoryObserver = new LogDirectoryObserver(logdir.getAbsolutePath());
72 }
f9a162a2
TB
73 }
74
75 @Override
76 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
77 {
78 View view = inflater.inflate(R.layout.log_fragment, null);
9e05f219
TB
79
80 mLogAdapter = new LogAdapter(getActivity());
81 mLog = view.findViewById(R.id.log);
82 mLog.setAdapter(mLogAdapter);
83
84 mScrollPosition = -1;
85 if (savedInstanceState != null)
86 {
87 mScrollPosition = savedInstanceState.getInt(SCROLL_POSITION, mScrollPosition);
88 }
f9a162a2
TB
89 return view;
90 }
91
9e05f219
TB
92 @Override
93 public void onSaveInstanceState(Bundle outState)
94 {
95 super.onSaveInstanceState(outState);
96
97 if (mLog.getLastVisiblePosition() == (mLogAdapter.getCount() - 1))
98 {
99 outState.putInt(SCROLL_POSITION, -1);
100 }
101 else
102 {
103 outState.putInt(SCROLL_POSITION, mLog.getFirstVisiblePosition());
104 }
105 }
106
f9a162a2
TB
107 @Override
108 public void onStart()
109 {
110 super.onStart();
9e05f219 111 mLogAdapter.restart();
ae10e8c4
TB
112 mDirectoryObserver.startWatching();
113 }
114
115 @Override
116 public void onStop()
117 {
118 super.onStop();
119 mDirectoryObserver.stopWatching();
9e05f219 120 mLogAdapter.stop();
ae10e8c4
TB
121 }
122
9e05f219 123 private class LogAdapter extends ArrayAdapter<String> implements Runnable
ae10e8c4 124 {
9e05f219
TB
125 private BufferedReader mReader;
126 private Thread mThread;
127 private volatile boolean mRunning;
128
129 public LogAdapter(@NonNull Context context)
f9a162a2 130 {
9e05f219 131 super(context, R.layout.log_list_item, R.id.log_line);
f9a162a2 132 }
9e05f219
TB
133
134 public void restart()
f9a162a2 135 {
9e05f219
TB
136 if (mRunning)
137 {
138 stop();
139 }
ae10e8c4 140
9e05f219 141 clear();
f9a162a2 142
9e05f219
TB
143 try
144 {
145 mReader = new BufferedReader(new FileReader(mLogFilePath));
146 }
147 catch (FileNotFoundException e)
148 {
149 mReader = new BufferedReader(new StringReader(""));
150 }
151 mRunning = true;
152 mThread = new Thread(this);
153 mThread.start();
f9a162a2 154 }
9e05f219
TB
155
156 public void stop()
f9a162a2 157 {
9e05f219
TB
158 try
159 {
160 mRunning = false;
161 mThread.interrupt();
162 mThread.join();
163 }
164 catch (InterruptedException e)
165 {
166 }
f9a162a2 167 }
f9a162a2 168
9e05f219
TB
169 private void logLines(final ArrayList<String> lines)
170 {
171 mLogHandler.post(() -> {
172 boolean scroll = getCount() == 0;
173 setNotifyOnChange(false);
74d44e15 174 for (String line : lines)
27cf3e66
TB
175 {
176 if (getResources().getConfiguration().screenWidthDp < 600)
177 { /* strip off prefix (month=3, day=2, time=8, thread=2, spaces=3) */
178 line = line.length() > 18 ? line.substring(18) : line;
179 }
9e05f219 180 add(line);
74d44e15 181 }
9e05f219
TB
182 notifyDataSetChanged();
183 if (scroll)
184 { /* scroll to the bottom or saved position after adding the first batch */
185 mLogHandler.post(() -> mLog.setSelection(mScrollPosition == -1 ? getCount() - 1 : mScrollPosition));
186 }
187 });
188 }
74d44e15 189
9e05f219
TB
190 @Override
191 public void run()
f9a162a2 192 {
9e05f219
TB
193 ArrayList<String> lines = null;
194
195 while (mRunning)
196 {
197 try
198 { /* this works as long as the file is not truncated */
199 String line = mReader.readLine();
200 if (line == null)
74d44e15 201 {
9e05f219
TB
202 if (lines != null)
203 {
204 logLines(lines);
205 lines = null;
206 }
207 /* wait until there is more to log */
208 Thread.sleep(1000);
74d44e15 209 }
9e05f219 210 else
74d44e15 211 {
9e05f219
TB
212 if (lines == null)
213 {
214 lines = new ArrayList<>();
215 }
216 lines.add(line);
74d44e15 217 }
9e05f219
TB
218 }
219 catch (Exception e)
220 {
221 break;
f9a162a2
TB
222 }
223 }
9e05f219 224 if (lines != null)
f9a162a2 225 {
9e05f219 226 logLines(lines);
f9a162a2
TB
227 }
228 }
229 }
ae10e8c4
TB
230
231 /**
232 * FileObserver that checks for changes regarding the log file. Since charon
233 * truncates it (for which there is no explicit event) we check for any modification
234 * to the file, keep track of the file size and reopen it if it got smaller.
235 */
236 private class LogDirectoryObserver extends FileObserver
237 {
c9761655
TB
238 private static final int mMask = FileObserver.CREATE | FileObserver.MODIFY | FileObserver.DELETE;
239 private final File mFile = new File(mLogFilePath);
240 private long mSize = mFile.length();
ae10e8c4 241
c9761655 242 @SuppressWarnings("deprecation")
ae10e8c4
TB
243 public LogDirectoryObserver(String path)
244 {
c9761655
TB
245 super(path, mMask);
246 }
247
248 @RequiresApi(api = Build.VERSION_CODES.Q)
249 public LogDirectoryObserver(File path)
250 {
251 super(path, mMask);
ae10e8c4
TB
252 }
253
254 @Override
255 public void onEvent(int event, String path)
256 {
257 if (path == null || !path.equals(CharonVpnService.LOG_FILE))
258 {
259 return;
260 }
261 switch (event)
262 { /* even though we only subscribed for these we check them,
263 * as strange events are sometimes received */
264 case FileObserver.CREATE:
265 case FileObserver.DELETE:
266 restartLogReader();
267 break;
268 case FileObserver.MODIFY:
269 /* if the size got smaller reopen the log file, as it was probably truncated */
270 long size = mFile.length();
271 if (size < mSize)
272 {
273 restartLogReader();
274 }
275 mSize = size;
276 break;
277 }
278 }
279
280 private void restartLogReader()
281 {
282 /* we are called from a separate thread, so we use the handler */
283 mLogHandler.post(new Runnable() {
284 @Override
285 public void run()
286 {
9e05f219 287 mLogAdapter.restart();
ae10e8c4
TB
288 }
289 });
290 }
291 }
f9a162a2 292}