diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e4150f1c..9f2b78cb 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -58,6 +58,10 @@
android:name=".activities.AboutActivity"
android:launchMode="singleTop"
android:parentActivityName=".activities.MainActivity" />
+
.
+ *
+ * Copyright 2020-21 - Emanuele Faranda
+ */
+
+package com.emanuelef.remote_capture.activities;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.Html;
+import android.text.method.LinkMovementMethod;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.emanuelef.remote_capture.CaptureService;
+import com.emanuelef.remote_capture.R;
+import com.emanuelef.remote_capture.Utils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class LogviewActivity extends BaseActivity {
+ private static final String TAG = "LogviewActivity";
+ private String mLogText;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setTitle(R.string.root_log);
+ setContentView(R.layout.logview_activity);
+
+ TextView logView = findViewById(R.id.log);
+ mLogText = readLog();
+
+ logView.setText(!mLogText.isEmpty() ? mLogText : getString(R.string.error));
+ }
+
+ private String readLog() {
+ try {
+ String logpath = CaptureService.getPcapdWorkingDir(this) + "/pcapd.log";
+ BufferedReader reader = new BufferedReader(new FileReader(logpath));
+
+ StringBuilder builder = new StringBuilder();
+ String line;
+
+ while((line = reader.readLine()) != null) {
+ builder.append(line);
+ builder.append("\n");
+ }
+
+ return builder.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return "";
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.copy_share_menu, menu);
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ int id = item.getItemId();
+
+ if(id == R.id.copy_to_clipboard) {
+ Utils.copyToClipboard(this, mLogText);
+ return true;
+ } else if(id == R.id.share) {
+ Intent intent = new Intent(android.content.Intent.ACTION_SEND);
+ intent.setType("text/plain");
+ intent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.root_log));
+ intent.putExtra(android.content.Intent.EXTRA_TEXT, mLogText);
+
+ startActivity(Intent.createChooser(intent, getResources().getString(R.string.share)));
+
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java
index 61120852..7d0369b7 100644
--- a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java
+++ b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java
@@ -51,6 +51,7 @@ import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
@@ -187,6 +188,11 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(GITHUB_PROJECT_URL + "/tree/" + verStr));
startActivity(browserIntent);
});
+
+ if(Prefs.isRootCaptureEnabled(mPrefs)) {
+ Menu navMenu = navView.getMenu();
+ navMenu.findItem(R.id.open_root_log).setVisible(true);
+ }
}
@Override
@@ -345,6 +351,9 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
startActivity(intent);
} else
Utils.showToast(this, R.string.capture_not_started);
+ } else if(id == R.id.open_root_log) {
+ Intent intent = new Intent(MainActivity.this, LogviewActivity.class);
+ startActivity(intent);
} else if (id == R.id.action_donate) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(DONATE_URL));
startActivity(browserIntent);
diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/StatsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/StatsActivity.java
index dba500f2..942c0696 100644
--- a/app/src/main/java/com/emanuelef/remote_capture/activities/StatsActivity.java
+++ b/app/src/main/java/com/emanuelef/remote_capture/activities/StatsActivity.java
@@ -145,12 +145,7 @@ public class StatsActivity extends BaseActivity {
if(id == R.id.copy_to_clipboard) {
String contents = Utils.table2Text(mTable);
-
- ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText(getString(R.string.stats), contents);
- clipboard.setPrimaryClip(clip);
-
- Utils.showToast(this, R.string.copied_to_clipboard);
+ Utils.copyToClipboard(this, contents);
return true;
} else if(id == R.id.share) {
String contents = Utils.table2Text(mTable);
diff --git a/app/src/main/jni/common/utils.c b/app/src/main/jni/common/utils.c
index 41dba7bf..617808a0 100644
--- a/app/src/main/jni/common/utils.c
+++ b/app/src/main/jni/common/utils.c
@@ -34,8 +34,8 @@ void set_log_level(int lvl) {
/* ******************************************************* */
-void log_android(int prio, const char *fmt, ...) {
- if(prio >= loglevel) {
+void log_android(int lvl, const char *fmt, ...) {
+ if(lvl >= loglevel) {
char line[1024];
va_list argptr;
@@ -43,10 +43,23 @@ void log_android(int prio, const char *fmt, ...) {
vsnprintf(line, sizeof(line), fmt, argptr);
va_end(argptr);
- __android_log_print(prio, logtag, "%s", line);
+ __android_log_print(lvl, logtag, "%s", line);
if(logcallback != NULL)
- logcallback(prio, line);
+ logcallback(lvl, line);
+ }
+}
+
+/* ******************************************************* */
+
+char loglvl2char(int lvl) {
+ switch (lvl) {
+ case ANDROID_LOG_DEBUG: return 'D';
+ case ANDROID_LOG_INFO: return 'I';
+ case ANDROID_LOG_WARN: return 'W';
+ case ANDROID_LOG_ERROR: return 'E';
+ case ANDROID_LOG_FATAL: return 'F';
+ default: return '?';
}
}
diff --git a/app/src/main/jni/common/utils.h b/app/src/main/jni/common/utils.h
index b2342c83..23a7cc3b 100644
--- a/app/src/main/jni/common/utils.h
+++ b/app/src/main/jni/common/utils.h
@@ -35,10 +35,11 @@ extern void (*logcallback)(int lvl, const char *msg);
#define log_e(...) log_android(ANDROID_LOG_ERROR, __VA_ARGS__)
#define log_f(...) log_android(ANDROID_LOG_FATAL, __VA_ARGS__)
-void log_android(int prio, const char *fmt, ...);
+void log_android(int lvl, const char *fmt, ...);
ssize_t xwrite(int fd, const void *buf, size_t count);
ssize_t xread(int fd, void *buf, size_t count);
void tupleSwapPeers(zdtun_5tuple_t *tuple);
+char loglvl2char(int lvl);
jclass jniFindClass(JNIEnv *env, const char *name);
jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature);
diff --git a/app/src/main/jni/pcapd/pcapd.c b/app/src/main/jni/pcapd/pcapd.c
index f838c7f0..b71c562d 100644
--- a/app/src/main/jni/pcapd/pcapd.c
+++ b/app/src/main/jni/pcapd/pcapd.c
@@ -71,6 +71,8 @@ typedef struct {
} pcapd_runtime_t;
static char errbuf[PCAP_ERRBUF_SIZE];
+static FILE *logf = NULL;
+static uint8_t no_logf = 0;
/* ******************************************************* */
@@ -84,6 +86,35 @@ static uint64_t bytes2mac(const uint8_t *buf) {
/* ******************************************************* */
+static void log_to_file(int lvl, const char *msg) {
+ char datetime[64];
+ struct tm res;
+ time_t now;
+
+ if(no_logf)
+ return;
+
+ if(logf == NULL) {
+ mode_t old_mask = umask(033);
+ logf = fopen(PCAPD_LOGFILE_PATH, "w");
+ umask(old_mask);
+
+ if(logf == NULL) {
+ log_e("Could not open log file[%d]: %s", errno, strerror(errno));
+ no_logf = 1;
+ return;
+ }
+ }
+
+ now = time(NULL);
+ strftime(datetime, sizeof(datetime), "%d/%b/%Y %H:%M:%S", localtime_r(&now, &res));
+
+ fprintf(logf, "[%c] %s - %s\n", loglvl2char(lvl), datetime, msg);
+ fflush(logf);
+}
+
+/* ******************************************************* */
+
static int get_iface_mac(const char *iface, uint64_t *mac) {
struct ifreq ifr;
int fd;
@@ -103,7 +134,7 @@ static int get_iface_mac(const char *iface, uint64_t *mac) {
/* ******************************************************* */
-int get_iface_ip(const char *iface, uint32_t *ip, uint32_t *netmask) {
+static int get_iface_ip(const char *iface, uint32_t *ip, uint32_t *netmask) {
struct ifreq ifr;
int fd;
int rv;
@@ -148,6 +179,9 @@ static void sighandler(__unused int signo) {
log_i("SIGTERM received, terminating");
unlink(PCAPD_PID);
+ if(logf)
+ fclose(logf);
+
exit(0);
}
@@ -493,7 +527,7 @@ static int run_pcap_dump(int uid_filter) {
}
if(FD_ISSET(rt.client, &fds)) {
- log_i("client closed");
+ log_i("Client closed");
break;
}
if(FD_ISSET(rt.nlsock, &fds)) {
@@ -593,6 +627,7 @@ static void usage() {
int main(int argc, char *argv[]) {
logtag = "pcapd";
+ logcallback = log_to_file;
if(argc < 2)
usage();
diff --git a/app/src/main/jni/pcapd/pcapd.h b/app/src/main/jni/pcapd/pcapd.h
index b6c9d98a..4ed52184 100644
--- a/app/src/main/jni/pcapd/pcapd.h
+++ b/app/src/main/jni/pcapd/pcapd.h
@@ -21,6 +21,7 @@
#define __PCAPD_H__
#define PCAPD_SOCKET_PATH "pcapsock"
+#define PCAPD_LOGFILE_PATH "pcapd.log"
#define PCAPD_PID "pcapd.pid"
#define PCAPD_FLAG_TX (1 << 0)
diff --git a/app/src/main/res/drawable/ic_bug_report.xml b/app/src/main/res/drawable/ic_bug_report.xml
new file mode 100644
index 00000000..c72d5c25
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bug_report.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/logview_activity.xml b/app/src/main/res/layout/logview_activity.xml
new file mode 100644
index 00000000..baab217a
--- /dev/null
+++ b/app/src/main/res/layout/logview_activity.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/nav_items.xml b/app/src/main/res/menu/nav_items.xml
index a8b9d849..95aa7d2f 100644
--- a/app/src/main/res/menu/nav_items.xml
+++ b/app/src/main/res/menu/nav_items.xml
@@ -9,6 +9,11 @@
android:id="@+id/action_stats"
android:title="@string/stats"
android:icon="@drawable/ic_list" />
+
- Capturing packets as root allows PCAPdroid to run with other VPN apps.
Donate
HTTP Request
+ Root Log