From 3dbb55fe35b12e5fcbf38e7ecf8de043b16327de Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Mon, 24 Dec 2012 07:47:50 +0100 Subject: Make send mini dump work with non Gmail Mail app, make google sending better placed --- AndroidManifest.xml | 5 +- openvpn/Android.mk | 2 +- openvpn/src/openvpn/breakpad.cpp | 29 +++ openvpn/src/openvpn/breakpad.h | 13 ++ openvpn/src/openvpn/error.c | 7 + openvpn/src/openvpn/openvpn.c | 10 +- openvpn/src/openvpn/socket.c | 2 +- res/menu/logmenu.xml | 6 - res/values/strings.xml | 3 + src/de/blinkt/openvpn/FileProvider.java | 206 ++++++++++++--------- src/de/blinkt/openvpn/LogWindow.java | 70 ------- src/de/blinkt/openvpn/MainActivity.java | 14 ++ src/de/blinkt/openvpn/OpenVPN.java | 58 +++--- src/de/blinkt/openvpn/OpenVPNThread.java | 21 ++- src/de/blinkt/openvpn/OpenVpnManagementThread.java | 10 +- src/de/blinkt/openvpn/SendDumpActivity.java | 60 ++++++ 16 files changed, 314 insertions(+), 202 deletions(-) create mode 100644 openvpn/src/openvpn/breakpad.cpp create mode 100644 openvpn/src/openvpn/breakpad.h create mode 100644 src/de/blinkt/openvpn/SendDumpActivity.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 8ccd1e96..564d4396 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -43,6 +43,7 @@ + @@ -121,8 +122,8 @@ + android:exported="true" + android:grantUriPermissions="true" /> \ No newline at end of file diff --git a/openvpn/Android.mk b/openvpn/Android.mk index 6307e5e0..c68bcccf 100644 --- a/openvpn/Android.mk +++ b/openvpn/Android.mk @@ -91,7 +91,7 @@ LOCAL_SRC_FILES:= \ src/openvpn/status.c \ src/openvpn/tun.c ifneq ($(TARGET_ARCH),mips) -LOCAL_SRC_FILES+=../jni/icsandroid.cpp +LOCAL_SRC_FILES+=src/openvpn/breakpad.cpp endif diff --git a/openvpn/src/openvpn/breakpad.cpp b/openvpn/src/openvpn/breakpad.cpp new file mode 100644 index 00000000..cfcc10a8 --- /dev/null +++ b/openvpn/src/openvpn/breakpad.cpp @@ -0,0 +1,29 @@ + +#include "breakpad.h" +#include "client/linux/handler/exception_handler.h" + + +static +bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, + void* context, + bool succeeded) { + printf("Dump path: %s\n", descriptor.path()); + fflush(stdout); + fflush(stderr); + return succeeded; +} + +static google_breakpad::MinidumpDescriptor* desc; +static google_breakpad::ExceptionHandler* eh; + +void breakpad_setup(void) +{ + printf("Initializing Google Breakpad!\n"); + desc = new google_breakpad::MinidumpDescriptor("/data/data/de.blinkt.openvpn/cache"); + eh = new google_breakpad::ExceptionHandler(*desc, NULL, DumpCallback, NULL, true,-1); +} + +void breakpad_dodump(void) +{ + eh->WriteMinidump(); +} diff --git a/openvpn/src/openvpn/breakpad.h b/openvpn/src/openvpn/breakpad.h new file mode 100644 index 00000000..84df62ab --- /dev/null +++ b/openvpn/src/openvpn/breakpad.h @@ -0,0 +1,13 @@ +#ifndef BUFFER_H +#define BUFFER_H + +#ifdef __cplusplus +extern "C" { +#endif + void breakpad_setup(void); + void breakpad_dodump(void); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/openvpn/src/openvpn/error.c b/openvpn/src/openvpn/error.c index ebe9a52d..98611a1b 100644 --- a/openvpn/src/openvpn/error.c +++ b/openvpn/src/openvpn/error.c @@ -57,6 +57,10 @@ #endif #endif +#ifdef GOOGLE_BREAKPAD +#include "breakpad.h" +#endif + /* Globals */ unsigned int x_debug_level; /* GLOBAL */ @@ -399,6 +403,9 @@ dont_mute (unsigned int flags) void assert_failed (const char *filename, int line) { +#ifdef GOOGLE_BREAKPAD + breakpad_dodump(); +#endif msg (M_FATAL, "Assertion failed at %s:%d", filename, line); } diff --git a/openvpn/src/openvpn/openvpn.c b/openvpn/src/openvpn/openvpn.c index 4bae5274..a177d9e8 100644 --- a/openvpn/src/openvpn/openvpn.c +++ b/openvpn/src/openvpn/openvpn.c @@ -41,6 +41,10 @@ #define P2P_CHECK_SIG() EVENT_LOOP_CHECK_SIGNAL (c, process_signal_p2p, c); +#ifdef GOOGLE_BREAKPAD +#include "breakpad.h" +#endif + static bool process_signal_p2p (struct context *c) { @@ -321,12 +325,10 @@ wmain (int argc, wchar_t *wargv[]) { #else int main (int argc, char *argv[]) { -#ifdef TARGET_ANDROID #ifdef GOOGLE_BREAKPAD -#include "../jni/icsandroid.h" - setup_breakpad(); -#endif + breakpad_setup(); #endif + return openvpn_main(argc, argv); } #endif diff --git a/openvpn/src/openvpn/socket.c b/openvpn/src/openvpn/socket.c index 29a15a63..9a33197c 100644 --- a/openvpn/src/openvpn/socket.c +++ b/openvpn/src/openvpn/socket.c @@ -1255,7 +1255,7 @@ resolve_remote (struct link_socket *sock, if (remote_dynamic) *remote_dynamic = NULL; } - else + /* else, quick hack to fix persistent-remote ....*/ { CLEAR (sock->info.lsa->actual); if(sock->info.lsa->current_remote) diff --git a/res/menu/logmenu.xml b/res/menu/logmenu.xml index ec2997b0..bb810df0 100644 --- a/res/menu/logmenu.xml +++ b/res/menu/logmenu.xml @@ -1,11 +1,5 @@ - Reconnection settings Number of seconds to wait between connection attempts. Seconds between connections + OpenVPN crashed unexpectedly. Please consider using the send Minidump option in the main menu + Send Minidump to developer + Send debugging information about last crash to developer diff --git a/src/de/blinkt/openvpn/FileProvider.java b/src/de/blinkt/openvpn/FileProvider.java index 1147ccf0..e86b544f 100644 --- a/src/de/blinkt/openvpn/FileProvider.java +++ b/src/de/blinkt/openvpn/FileProvider.java @@ -24,13 +24,15 @@ import java.io.IOException; import java.io.InputStream; import android.content.ContentProvider; -import android.content.ContentValues; import android.content.ContentProvider.PipeDataWriter; +import android.content.ContentValues; import android.content.res.AssetFileDescriptor; import android.database.Cursor; +import android.database.MatrixCursor; import android.net.Uri; import android.os.Bundle; import android.os.ParcelFileDescriptor; +import android.provider.OpenableColumns; import android.util.Log; /** @@ -38,92 +40,118 @@ import android.util.Log; * our .apk. */ public class FileProvider extends ContentProvider - implements PipeDataWriter { - @Override - public boolean onCreate() { - return true; - } - - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - // Don't support queries. - return null; - } - - @Override - public Uri insert(Uri uri, ContentValues values) { - // Don't support inserts. - return null; - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - // Don't support deletes. - return 0; - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - // Don't support updates. - return 0; - } - - @Override - public String getType(Uri uri) { - // For this sample, assume all files are .apks. - return "application/octet-stream"; - } - - @Override - public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { - // Try to open an asset with the given name. - String path = uri.getPath(); - if(path.startsWith("/")) - path = path.replaceFirst("/", ""); - - // I think this already random enough, no need for magic secure cookies - // 1f9563a4-a1f5-2165-255f2219-111823ef.dmp - if (!path.matches("^[0-9a-z-.]*$")) - throw new FileNotFoundException("url not in expect format " + uri); - - try { - - File cachedir = getContext().getCacheDir(); - File dumpfile = new File(cachedir,path); - InputStream is = new FileInputStream(dumpfile); - // Start a new thread that pipes the stream data back to the caller. - return new AssetFileDescriptor( - openPipeHelper(uri, null, null, is, this), 0, - dumpfile.length()); - } catch (IOException e) { - FileNotFoundException fnf = new FileNotFoundException("Unable to open minidump " + uri); - throw fnf; - } - } - - @Override - public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType, - Bundle opts, InputStream args) { - // Transfer data from the asset to the pipe the client is reading. - byte[] buffer = new byte[8192]; - int n; - FileOutputStream fout = new FileOutputStream(output.getFileDescriptor()); - try { - while ((n=args.read(buffer)) >= 0) { - fout.write(buffer, 0, n); - } - } catch (IOException e) { - Log.i("OpenVPNFileProvider", "Failed transferring", e); - } finally { - try { - args.close(); - } catch (IOException e) { - } - try { - fout.close(); - } catch (IOException e) { - } - } - } +implements PipeDataWriter { + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + try { + File dumpfile = getFileFromURI(uri); + + + MatrixCursor c = new MatrixCursor(projection); + + Object[] row = new Object[projection.length]; + int i=0; + for (String r:projection) { + if(r.equals(OpenableColumns.SIZE)) + row[i] = dumpfile.length(); + if(r.equals(OpenableColumns.DISPLAY_NAME)) + row[i] = dumpfile.getName(); + i++; + } + c.addRow(row); + return c; + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } + + + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + // Don't support inserts. + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + // Don't support deletes. + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + // Don't support updates. + return 0; + } + + @Override + public String getType(Uri uri) { + // For this sample, assume all files are .apks. + return "application/octet-stream"; + } + + @Override + public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { + File dumpfile = getFileFromURI(uri); + + try { + + InputStream is = new FileInputStream(dumpfile); + // Start a new thread that pipes the stream data back to the caller. + return new AssetFileDescriptor( + openPipeHelper(uri, null, null, is, this), 0, + dumpfile.length()); + } catch (IOException e) { + FileNotFoundException fnf = new FileNotFoundException("Unable to open minidump " + uri); + throw fnf; + } + } + + private File getFileFromURI(Uri uri) throws FileNotFoundException { + // Try to open an asset with the given name. + String path = uri.getPath(); + if(path.startsWith("/")) + path = path.replaceFirst("/", ""); + + // I think this already random enough, no need for magic secure cookies + // 1f9563a4-a1f5-2165-255f2219-111823ef.dmp + if (!path.matches("^[0-9a-z-.]*(dmp|dmp.log)$")) + throw new FileNotFoundException("url not in expect format " + uri); + File cachedir = getContext().getCacheDir(); + File dumpfile = new File(cachedir,path); + return dumpfile; + } + + @Override + public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType, + Bundle opts, InputStream args) { + // Transfer data from the asset to the pipe the client is reading. + byte[] buffer = new byte[8192]; + int n; + FileOutputStream fout = new FileOutputStream(output.getFileDescriptor()); + try { + while ((n=args.read(buffer)) >= 0) { + fout.write(buffer, 0, n); + } + } catch (IOException e) { + Log.i("OpenVPNFileProvider", "Failed transferring", e); + } finally { + try { + args.close(); + } catch (IOException e) { + } + try { + fout.close(); + } catch (IOException e) { + } + } + } } diff --git a/src/de/blinkt/openvpn/LogWindow.java b/src/de/blinkt/openvpn/LogWindow.java index 3c0f7246..790e143a 100644 --- a/src/de/blinkt/openvpn/LogWindow.java +++ b/src/de/blinkt/openvpn/LogWindow.java @@ -1,9 +1,5 @@ package de.blinkt.openvpn; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; import java.util.Vector; import android.app.AlertDialog; @@ -16,7 +12,6 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.database.DataSetObserver; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Handler.Callback; @@ -227,8 +222,6 @@ public class LogWindow extends ListActivity implements StateListener { } else if(item.getItemId()==R.id.info) { if(mBconfig==null) OpenVPN.triggerLogBuilderConfig(); - } else if(item.getItemId()==R.id.minidump) { - emailMiniDumps(); } else if(item.getItemId()==R.id.send) { ladapter.shareLog(); @@ -258,72 +251,9 @@ public class LogWindow extends ListActivity implements StateListener { public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.logmenu, menu); - - - - if(getLastestDump()==null) - menu.removeItem(R.id.minidump); - return true; } - private File getLastestDump() { - long newestDumpTime=0; - File newestDumpFile=null; - - for(File f:getCacheDir().listFiles()) { - if(!f.getName().endsWith(".dmp")) - continue; - - if (newestDumpTime < f.lastModified()) { - newestDumpTime = f.lastModified(); - newestDumpFile=f; - } - } - return newestDumpFile; - } - - - public void emailMiniDumps() - { - //need to "send multiple" to get more than one attachment - final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE); - emailIntent.setType("*/*"); - emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, - new String[]{"Arne Schwabe "}); - emailIntent.putExtra(Intent.EXTRA_SUBJECT, "OpenVPN Minidump"); - - - ArrayList textarraylist = new ArrayList(); - textarraylist.add("Please describe the issue you have experienced"); - emailIntent.putExtra(Intent.EXTRA_TEXT, textarraylist); - - - ArrayList uris = new ArrayList(); - - File ldump = getLastestDump(); - if(ldump==null) { - OpenVPN.logError("No Minidump found!"); - } - - uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/" + ldump.getName())); - uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/openvpn.log")); - - - try { - FileWriter logout = new FileWriter(new File(getCacheDir(),"openvpn.log")); - logout.write(ladapter.getLogStr()); - logout.close(); - - } catch (IOException e1) { - OpenVPN.logError("Error writing log: " + e1.getLocalizedMessage()); - } - - //emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); - startActivity(emailIntent); - } - @Override protected void onResume() { diff --git a/src/de/blinkt/openvpn/MainActivity.java b/src/de/blinkt/openvpn/MainActivity.java index 9b329817..32cf575b 100644 --- a/src/de/blinkt/openvpn/MainActivity.java +++ b/src/de/blinkt/openvpn/MainActivity.java @@ -1,8 +1,11 @@ package de.blinkt.openvpn; +import java.io.File; +import java.util.ArrayList; import java.util.List; import android.content.Intent; +import android.net.Uri; import android.preference.PreferenceActivity; public class MainActivity extends PreferenceActivity { @@ -19,6 +22,15 @@ public class MainActivity extends PreferenceActivity { translation.summary = translatedby; target.add(translation); } + + if(SendDumpActivity.getLastestDump(this)!=null) { + Header sendDump = new Header(); + sendDump.titleRes = R.string.send_minidump; + sendDump.summaryRes = R.string.send_minidump_summary; + sendDump.intent = new Intent(this,SendDumpActivity.class); + target.add(sendDump); + } + } @Override @@ -29,4 +41,6 @@ public class MainActivity extends PreferenceActivity { } + + } diff --git a/src/de/blinkt/openvpn/OpenVPN.java b/src/de/blinkt/openvpn/OpenVPN.java index 48623865..0ae681bc 100644 --- a/src/de/blinkt/openvpn/OpenVPN.java +++ b/src/de/blinkt/openvpn/OpenVPN.java @@ -7,10 +7,10 @@ import android.content.Context; import android.os.Build; public class OpenVPN { - + public static LinkedList logbuffer; - + private static Vector logListener; private static Vector stateListener; private static String[] mBconfig; @@ -18,14 +18,14 @@ public class OpenVPN { private static String mLaststatemsg; private static String mLaststate; - + static { logbuffer = new LinkedList(); logListener = new Vector(); stateListener = new Vector(); logInformation(); } - + static class LogItem { public static final int ERROR = 1; public static final int INFO = 2; @@ -36,20 +36,20 @@ public class OpenVPN { private int mRessourceId; // Default log priority int mLevel = INFO; - + public LogItem(int ressourceId, Object[] args) { - mRessourceId = ressourceId; - mArgs = args; + mRessourceId = ressourceId; + mArgs = args; } - + public LogItem(int loglevel,int ressourceId, Object[] args) { - mRessourceId = ressourceId; - mArgs = args; - mLevel = loglevel; - } + mRessourceId = ressourceId; + mArgs = args; + mLevel = loglevel; + } + - public LogItem(String message) { mMessage = message; } @@ -70,28 +70,32 @@ public class OpenVPN { if(mMessage !=null) { return mMessage; } else { - if(mArgs == null) - return c.getString(mRessourceId); - else - return c.getString(mRessourceId,mArgs); + if(c!=null) { + if(mArgs == null) + return c.getString(mRessourceId); + else + return c.getString(mRessourceId,mArgs); + } else { + return String.format("Log (no context) resid %d", mRessourceId); + } } } } - + private static final int MAXLOGENTRIES = 200; public static final String MANAGMENT_PREFIX = "M:"; - + public interface LogListener { void newLog(LogItem logItem); } - + public interface StateListener { void updateState(String state, String logmessage); } @@ -108,7 +112,7 @@ public class OpenVPN { } private static void logInformation() { - + logInfo(R.string.mobile_info,Build.MODEL, Build.BOARD,Build.BRAND,Build.VERSION.SDK_INT); } @@ -120,7 +124,7 @@ public class OpenVPN { logListener.remove(ll); } - + synchronized static void addStateListener(StateListener sl){ stateListener.add(sl); if(mLaststate!=null) @@ -156,7 +160,7 @@ public class OpenVPN { public synchronized static void updateStateString(String state, String msg) { mLaststate= state; mLaststatemsg = msg; - + for (StateListener sl : stateListener) { sl.updateState(state,msg); } @@ -174,7 +178,7 @@ public class OpenVPN { logbuffer.addLast(logItem); if(logbuffer.size()>MAXLOGENTRIES) logbuffer.removeFirst(); - + for (LogListener ll : logListener) { ll.newLog(logItem); } @@ -182,7 +186,7 @@ public class OpenVPN { public static void logError(String msg) { newlogItem(new LogItem(LogItem.ERROR, msg)); - + } public static void logError(int ressourceId) { @@ -191,6 +195,6 @@ public class OpenVPN { public static void logError(int ressourceId, Object... args) { newlogItem(new LogItem(LogItem.ERROR, ressourceId,args)); } - - + + } diff --git a/src/de/blinkt/openvpn/OpenVPNThread.java b/src/de/blinkt/openvpn/OpenVPNThread.java index 13b8688b..7d58552a 100644 --- a/src/de/blinkt/openvpn/OpenVPNThread.java +++ b/src/de/blinkt/openvpn/OpenVPNThread.java @@ -1,19 +1,24 @@ package de.blinkt.openvpn; import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.LinkedList; import android.util.Log; +import de.blinkt.openvpn.OpenVPN.LogItem; public class OpenVPNThread implements Runnable { + private static final String DUMP_PATH_STRING = "Dump path: "; private static final String TAG = "OpenVPN"; private String[] mArgv; private Process mProcess; private String mNativeDir; private OpenVpnService mService; + private String mDumpPath; public OpenVPNThread(OpenVpnService service,String[] argv, String nativelibdir) { @@ -49,7 +54,18 @@ public class OpenVPNThread implements Runnable { OpenVPN.logError("Process exited with exit value " + exitvalue); OpenVPN.updateStateString("NOPROCESS","No process running."); - + if(mDumpPath!=null) { + try { + BufferedWriter logout = new BufferedWriter(new FileWriter(mDumpPath + ".log")); + for(LogItem li :OpenVPN.getlogbuffer()){ + logout.write(li.getString(null) + "\n"); + } + logout.close(); + OpenVPN.logError(R.string.minidump_generated); + } catch (IOException e) { + OpenVPN.logError("Writing minidump log: " +e.getLocalizedMessage()); + } + } mService.processDied(); Log.i(TAG, "Exiting"); @@ -89,6 +105,9 @@ public class OpenVPNThread implements Runnable { while(true) { String logline = br.readLine(); + if (logline.startsWith(DUMP_PATH_STRING)) + mDumpPath = logline.substring(DUMP_PATH_STRING.length()); + if(logline==null) { return; } diff --git a/src/de/blinkt/openvpn/OpenVpnManagementThread.java b/src/de/blinkt/openvpn/OpenVpnManagementThread.java index d15e6a33..9926ba13 100644 --- a/src/de/blinkt/openvpn/OpenVpnManagementThread.java +++ b/src/de/blinkt/openvpn/OpenVpnManagementThread.java @@ -39,7 +39,8 @@ public class OpenVpnManagementThread implements Runnable { private long mLastOut=0; private LocalServerSocket mServerSocket; private boolean mReleaseHold=true; - private boolean mWaitingForRelease=false; + private boolean mWaitingForRelease=false; + private long mLastHoldRelease=0; private static Vector active=new Vector(); @@ -223,8 +224,15 @@ public class OpenVpnManagementThread implements Runnable { } } private void releaseHoldCmd() { + if ((System.currentTimeMillis()- mLastHoldRelease) < 5000) { + try { + Thread.sleep(3000); + } catch (InterruptedException e) {} + + } mWaitingForRelease=false; mReleaseHold=true; + mLastHoldRelease = System.currentTimeMillis(); managmentCommand("hold release\n"); managmentCommand("bytecount " + mBytecountinterval + "\n"); managmentCommand("state on\n"); diff --git a/src/de/blinkt/openvpn/SendDumpActivity.java b/src/de/blinkt/openvpn/SendDumpActivity.java new file mode 100644 index 00000000..8a09b535 --- /dev/null +++ b/src/de/blinkt/openvpn/SendDumpActivity.java @@ -0,0 +1,60 @@ +package de.blinkt.openvpn; + +import java.io.File; +import java.util.ArrayList; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +public class SendDumpActivity extends Activity { + + protected void onStart() { + super.onStart(); + emailMiniDumps(); + finish(); + }; + + public void emailMiniDumps() + { + //need to "send multiple" to get more than one attachment + final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE); + emailIntent.setType("*/*"); + emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, + new String[]{"Arne Schwabe "}); + emailIntent.putExtra(Intent.EXTRA_SUBJECT, "OpenVPN Minidump"); + + emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe the issue you have experienced"); + + ArrayList uris = new ArrayList(); + + File ldump = getLastestDump(this); + if(ldump==null) { + OpenVPN.logError("No Minidump found!"); + } + + uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/" + ldump.getName())); + uris.add(Uri.parse("content://de.blinkt.openvpn.FileProvider/" + ldump.getName() + ".log")); + + emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); + startActivity(emailIntent); + } + + static public File getLastestDump(Context c) { + long newestDumpTime=0; + File newestDumpFile=null; + + for(File f:c.getCacheDir().listFiles()) { + if(!f.getName().endsWith(".dmp")) + continue; + + if (newestDumpTime < f.lastModified()) { + newestDumpTime = f.lastModified(); + newestDumpFile=f; + } + } + return newestDumpFile; + } +} -- cgit v1.2.3