From ce27722b1ab0871bb3bec2805c26f2af034df688 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Tue, 10 Dec 2019 16:48:18 +0100 Subject: [PATCH 01/16] add ServiceExceptionHandler into utils package --- .idea/codeStyles/Project.xml | 116 ++++++++++++++++++ .../drive/utils/ServiceExceptionHandler.java | 91 ++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..681f41ae --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java new file mode 100644 index 00000000..8c6dc4e3 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java @@ -0,0 +1,91 @@ +/* + * Copyright © Vincent Bourgmayer (/e/ foundation). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + */ +package foundation.e.drive.utils; +import android.app.Service; +import android.os.Environment; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.Thread.UncaughtExceptionHandler; + +/** + * @author Vincent Bourgmayer + */ +public class ServiceExceptionHandler implements UncaughtExceptionHandler{ + private UncaughtExceptionHandler defaultUEH; + + Service service; + + public ServiceExceptionHandler(Service service) { + this.service = service; + defaultUEH = Thread.getDefaultUncaughtExceptionHandler(); + } + + @Override + public void uncaughtException(Thread t, Throwable e) { + + if(isExternalStorageAvailable() && !isExternalStorageReadOnly()){ + //Get TimeStamp + Long timestamp = System.currentTimeMillis(); + + //Create a new file that user can sent to us + String fileName = "eDrive-crash-"+timestamp+".log"; + File downloadDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()); + File logFile = new File(downloadDir, fileName); + try { + FileOutputStream fos = new FileOutputStream(logFile); + fos.write(getStackTraceAsString(e).getBytes()); + fos.close(); + } catch (IOException exception) { + exception.printStackTrace(); + } + } + + //source: https://stackoverflow.com/questions/9050962/rethrow-uncaughtexceptionhandler-exception-after-logging-it/9050990#9050990 + if(defaultUEH != null){ + defaultUEH.uncaughtException(t, e); + }else{ + Log.d("ServiceExceptionHandler", "/e/ Drive has crashed and there is no ExceptionHandler"); + System.exit(1); //Kill /e/ Drive... + } + } + + //source: https://www.journaldev.com/9400/android-external-storage-read-write-save-file + private static boolean isExternalStorageAvailable() { + String extStorageState = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(extStorageState)) { + return true; + } + return false; + } + + //source: https://www.journaldev.com/9400/android-external-storage-read-write-save-file + private static boolean isExternalStorageReadOnly() { + String extStorageState = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState)) { + return true; + } + return false; + } + + /** + * Return the stackTrace of the exception as a String + * @param e the exception + * @return the Stacktrace as a string + */ + private String getStackTraceAsString(Throwable e){ + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + return sw.toString(); + } +} \ No newline at end of file -- GitLab From bebbbc91b77476111e12d48ef9cd59d14f179254 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Tue, 10 Dec 2019 17:41:29 +0100 Subject: [PATCH 02/16] use ServiceExceptionHandler in OMS, ObserverService & initializer --- .../e/drive/services/InitializerService.java | 2 ++ .../foundation/e/drive/services/ObserverService.java | 11 ++++------- .../e/drive/services/OperationManagerService.java | 3 +++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 546e5c09..8cedb2cc 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -37,6 +37,7 @@ import foundation.e.drive.receivers.ScreenOffReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.JobUtils; +import foundation.e.drive.utils.ServiceExceptionHandler; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; import static foundation.e.drive.utils.AppConstants.INITIALFOLDERS_NUMBER; @@ -67,6 +68,7 @@ public class InitializerService extends Service implements OnRemoteOperationList @Override public int onStartCommand( Intent intent, int flags, int startId ) { Log.i(TAG, "onStartCommand(...)"); + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); //Get account SharedPreferences prefs = this.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ); diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index 1afcb82a..ae84a86d 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -49,6 +49,8 @@ import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.DavClientProvider; import foundation.e.drive.utils.JobUtils; +import foundation.e.drive.utils.ServiceExceptionHandler; + import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; import static foundation.e.drive.utils.AppConstants.INITIALIZATION_HAS_BEEN_DONE; @@ -74,16 +76,11 @@ public class ObserverService extends Service implements OnRemoteOperationListene super.onDestroy(); this.mSyncedFolders = null; } - - @Override - public void onCreate() { - Log.i(TAG, "onCreate()"); - super.onCreate(); - } - + @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand("+startId+")"); + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); diff --git a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java index 786160bb..b0ee9313 100644 --- a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java +++ b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java @@ -40,6 +40,7 @@ import foundation.e.drive.operations.UploadFileOperation; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.DavClientProvider; +import foundation.e.drive.utils.ServiceExceptionHandler; /** * @author Vincent Bourgmayer @@ -247,6 +248,8 @@ public class OperationManagerService extends Service implements OnRemoteOperatio public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand()"); + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); + Bundle extras = intent.getExtras(); Log.d(TAG, "OperationManagerService recieved "+(extras == null ? "null extras": extras.size()+" operations to perform") ); -- GitLab From 74821d3ed34b6d3313083543e41ab9de01838c32 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Wed, 11 Dec 2019 16:01:43 +0100 Subject: [PATCH 03/16] fix generation of multiple file --- .../e/drive/services/InitializerService.java | 7 ++++++- .../e/drive/services/ObserverService.java | 7 +++++-- .../drive/services/OperationManagerService.java | 16 +++++----------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 8cedb2cc..7289b42a 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -68,7 +68,12 @@ public class InitializerService extends Service implements OnRemoteOperationList @Override public int onStartCommand( Intent intent, int flags, int startId ) { Log.i(TAG, "onStartCommand(...)"); - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); + if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ + Log.d("ObserverService", "ServiceExceptionHandler already set!"); + }else{ + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); + } + //Get account SharedPreferences prefs = this.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ); diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index ae84a86d..f62c77cd 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -80,8 +80,11 @@ public class ObserverService extends Service implements OnRemoteOperationListene @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand("+startId+")"); - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); - + if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ + Log.d("ObserverService", "ServiceExceptionHandler already set!"); + }else{ + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); + } SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); diff --git a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java index b0ee9313..12e9268d 100644 --- a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java +++ b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java @@ -63,15 +63,6 @@ public class OperationManagerService extends Service implements OnRemoteOperatio @Override public void onDestroy() { Log.i(TAG, "onDestroy()"); - /*this.mOperationsQueue.clear(); - for(int i =-1, size = mThreadPool.length;++i < size;){ - try{ - if(mThreadPool[i] != null) - mThreadPool[i].interrupt();} - catch(Exception e){ - Log.e(TAG, e.toString()); - } - }*/ super.onDestroy(); } @@ -248,8 +239,11 @@ public class OperationManagerService extends Service implements OnRemoteOperatio public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand()"); - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); - + if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ + Log.d("ObserverService", "ServiceExceptionHandler already set!"); + }else{ + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); + } Bundle extras = intent.getExtras(); Log.d(TAG, "OperationManagerService recieved "+(extras == null ? "null extras": extras.size()+" operations to perform") ); -- GitLab From 135708d1e5bf4b68f171763700cc89380c41ebd6 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Fri, 13 Dec 2019 09:00:01 +0100 Subject: [PATCH 04/16] add method in CommonUtils to set the ServiceUncaughtExceptionHandler --- .../foundation/e/drive/utils/CommonUtils.java | 16 ++++++++++++++++ .../e/drive/utils/ServiceExceptionHandler.java | 10 ++++++++++ 2 files changed, 26 insertions(+) diff --git a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java index 421a58bc..398a16e5 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -12,6 +12,7 @@ package foundation.e.drive.utils; import android.accounts.Account; import android.accounts.AccountManager; +import android.app.Service; import android.content.ContentResolver; import android.content.Context; import android.media.MediaScannerConnection; @@ -39,6 +40,21 @@ import static foundation.e.drive.utils.AppConstants.SETTINGSYNC_PROVIDER_AUTHORI public abstract class CommonUtils { final private static String TAG = CommonUtils.class.getSimpleName(); + /** + * Set ServiceUncaughtExceptionHandler to be the MainThread Exception Handler + * Or update the service which use it + * @param service current service + */ + public static void setServiceUnCaughtExceptionHandler(Service service){ + + Thread.UncaughtExceptionHandler defaultUEH = Thread.getDefaultUncaughtExceptionHandler(); + if(defaultUEH.getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ + Log.d("ObserverService", "ServiceExceptionHandler already set!"); + ((ServiceExceptionHandler) defaultUEH).setService(service); + }else{ + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(service)); + } + } /** * Unregister from screeOffReceiver component diff --git a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java index 8c6dc4e3..b790e6f8 100644 --- a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java +++ b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java @@ -23,8 +23,18 @@ import java.lang.Thread.UncaughtExceptionHandler; public class ServiceExceptionHandler implements UncaughtExceptionHandler{ private UncaughtExceptionHandler defaultUEH; + + Service service; + /** + * Update the service which use this handler + * @param service current running service + */ + public void setService(Service service) { + this.service = service; + } + public ServiceExceptionHandler(Service service) { this.service = service; defaultUEH = Thread.getDefaultUncaughtExceptionHandler(); -- GitLab From 46b51eb7a83e3e32a34fc84c5c2dccbe825794dc Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Fri, 13 Dec 2019 09:03:12 +0100 Subject: [PATCH 05/16] Check in ServiceUEH if service is OMS and then disable OMS_IS_WORKING --- .../drive/utils/ServiceExceptionHandler.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java index b790e6f8..9bdfa088 100644 --- a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java +++ b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java @@ -7,6 +7,7 @@ */ package foundation.e.drive.utils; import android.app.Service; +import android.content.Context; import android.os.Environment; import android.util.Log; @@ -17,13 +18,14 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.lang.Thread.UncaughtExceptionHandler; +import foundation.e.drive.services.OperationManagerService; + /** * @author Vincent Bourgmayer */ public class ServiceExceptionHandler implements UncaughtExceptionHandler{ private UncaughtExceptionHandler defaultUEH; - - + private final static String TAG = ServiceExceptionHandler.class.getSimpleName(); Service service; @@ -42,6 +44,14 @@ public class ServiceExceptionHandler implements UncaughtExceptionHandler{ @Override public void uncaughtException(Thread t, Throwable e) { + Log.d(TAG, "Service class: "+service.getClass().getSimpleName()); + //IF OMS is crashing, set settings that it runs to false; + if(service.getClass().getSimpleName().equals(OperationManagerService.class.getSimpleName())){ + service.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) + .edit() + .putBoolean(AppConstants.KEY_OMS_IS_WORKING, false) + .apply(); + } if(isExternalStorageAvailable() && !isExternalStorageReadOnly()){ //Get TimeStamp @@ -49,22 +59,26 @@ public class ServiceExceptionHandler implements UncaughtExceptionHandler{ //Create a new file that user can sent to us String fileName = "eDrive-crash-"+timestamp+".log"; - File downloadDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()); + + File downloadDir = service.getApplication().getExternalFilesDir("Logs"); File logFile = new File(downloadDir, fileName); try { + FileOutputStream fos = new FileOutputStream(logFile); + fos.write(service.getClass().getSimpleName().getBytes()); fos.write(getStackTraceAsString(e).getBytes()); fos.close(); + logFile.setReadable(true, false); + } catch (IOException exception) { exception.printStackTrace(); } } - //source: https://stackoverflow.com/questions/9050962/rethrow-uncaughtexceptionhandler-exception-after-logging-it/9050990#9050990 if(defaultUEH != null){ defaultUEH.uncaughtException(t, e); }else{ - Log.d("ServiceExceptionHandler", "/e/ Drive has crashed and there is no ExceptionHandler"); + Log.d(TAG, "/e/ Drive has crashed and there is no ExceptionHandler"); System.exit(1); //Kill /e/ Drive... } } -- GitLab From 563c996586125b68ae9dfe0b03fb029698ef0e1f Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Fri, 13 Dec 2019 09:05:19 +0100 Subject: [PATCH 06/16] use CommonUtils.setServiceUncaughtException(Service) in concerned service --- .../foundation/e/drive/services/InitializerService.java | 6 +----- .../java/foundation/e/drive/services/ObserverService.java | 8 +++----- .../e/drive/services/OperationManagerService.java | 7 ++----- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 7289b42a..607edf9d 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -68,11 +68,7 @@ public class InitializerService extends Service implements OnRemoteOperationList @Override public int onStartCommand( Intent intent, int flags, int startId ) { Log.i(TAG, "onStartCommand(...)"); - if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ - Log.d("ObserverService", "ServiceExceptionHandler already set!"); - }else{ - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); - } + CommonUtils.setServiceUnCaughtExceptionHandler(this); //Get account SharedPreferences prefs = this.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ); diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index f62c77cd..187563b8 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -80,11 +80,9 @@ public class ObserverService extends Service implements OnRemoteOperationListene @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand("+startId+")"); - if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ - Log.d("ObserverService", "ServiceExceptionHandler already set!"); - }else{ - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); - } + + CommonUtils.setServiceUnCaughtExceptionHandler(this); + SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); diff --git a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java index 12e9268d..eb0999c2 100644 --- a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java +++ b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java @@ -239,11 +239,8 @@ public class OperationManagerService extends Service implements OnRemoteOperatio public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand()"); - if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ - Log.d("ObserverService", "ServiceExceptionHandler already set!"); - }else{ - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); - } + CommonUtils.setServiceUnCaughtExceptionHandler(this); + Bundle extras = intent.getExtras(); Log.d(TAG, "OperationManagerService recieved "+(extras == null ? "null extras": extras.size()+" operations to perform") ); -- GitLab From dae220826779d4170ef01263e1ecc98df22b5b93 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Sat, 14 Dec 2019 15:25:01 +0100 Subject: [PATCH 07/16] add method to remove oldestCrashlogs. add CrashlogsFileFilter with unitTest class. --- .../fileFilters/CrashlogsFileFilter.java | 4 +++ .../e/drive/services/ObserverService.java | 25 +++++++++++++++++++ .../CrashlogFileFilterTest.java | 4 +++ 3 files changed, 33 insertions(+) create mode 100644 app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java create mode 100644 app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java diff --git a/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java b/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java new file mode 100644 index 00000000..791312e1 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java @@ -0,0 +1,4 @@ +package foundation.e.drive.fileFilters; + +public class CrashlogsFileFilter { +} diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index 187563b8..bca4232f 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -37,6 +37,7 @@ import java.util.ListIterator; import java.util.Map; import foundation.e.drive.database.DbHelper; +import foundation.e.drive.fileFilters.CrashlogsFileFilter; import foundation.e.drive.fileFilters.FileFilterFactory; import foundation.e.drive.fileFilters.OnlyFileFilter; import foundation.e.drive.models.SyncedFolder; @@ -151,12 +152,36 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.i(TAG, "begin()"); this.isWorking = true; clearCachedFile(); + deleteOldestCrashlogs(); startScan(true); } + /** + * This method remove all the crash-logs file + * in external dir that are 10 days or more old. + */ + private void deleteOldestCrashlogs(){ + Log.i(TAG, "deleteOldestCrashLogs()"); + File[] fileToRemove = getExternalFilesDir(ServiceExceptionHandler.CRASH_LOG_FOLDER) + .listFiles(new CrashlogsFileFilter()); + + int counter = 0; + for (File file : fileToRemove) { + try { + file.delete(); + ++counter; + }catch (SecurityException e){ + e.printStackTrace(); + } + } + Log.d(TAG, counter+" old crashlogs file.s deleted"); + } + + /** * Clear cached file unused: * remove each cached file which isn't in OperationManagerService.lockedSyncedFileState(); + * @TODO rewrite this method! */ private void clearCachedFile(){ Log.i(TAG, "clearCachedFile()"); diff --git a/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java b/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java new file mode 100644 index 00000000..c0b7086a --- /dev/null +++ b/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java @@ -0,0 +1,4 @@ +package foundation.e.drive.Test.FileFilterTest; + +public class CrashlogFileFilterTest { +} -- GitLab From 8ad8dc6e2cbfe22fea28f53769190eb84e18bec1 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Sat, 14 Dec 2019 15:26:44 +0100 Subject: [PATCH 08/16] add method to remove oldestCrashlogs. add CrashlogsFileFilter with unitTest class. --- .../fileFilters/CrashlogsFileFilter.java | 42 ++++++++++++++++- .../drive/utils/ServiceExceptionHandler.java | 9 ++-- .../CrashlogFileFilterTest.java | 46 ++++++++++++++++++- 3 files changed, 91 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java b/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java index 791312e1..5a249733 100644 --- a/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java +++ b/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java @@ -1,4 +1,42 @@ package foundation.e.drive.fileFilters; -public class CrashlogsFileFilter { -} +import java.io.File; +import java.io.FileFilter; + +import foundation.e.drive.utils.ServiceExceptionHandler; + +public class CrashlogsFileFilter implements FileFilter { + private final static long max_timestamp_delta = 864000000; //10 days in ms (240*3600*1000) + + @Override + public boolean accept(File pathname) { + String fileTimestamp = extractTimestamp(pathname.getName(), + ServiceExceptionHandler.LOG_FILE_NAME_PREFIX, + ServiceExceptionHandler.LOG_FILE_EXTENSION); + + long timestamp; + try { + timestamp = Long.parseLong(fileTimestamp); + }catch (NumberFormatException e){ + //Can't parse the extracted timestamp + //This file has not the expected name. It must be removed + return true; + } + + //if current Date - file date >= max deta allowed + return ((System.currentTimeMillis() - timestamp ) >= max_timestamp_delta); + } + + /** + * Extract the timestamp from the name of the file + * UnitTested! + * @param fileName Filename + * @param prefix prefix to ignore + * @param extension extension to ignore + * @return the timestamp extracted from the name + */ + private String extractTimestamp(String fileName, String prefix, String extension){ + return fileName.substring(prefix.length(), (fileName.length() - extension.length())); + } + +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java index 9bdfa088..250e791b 100644 --- a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java +++ b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java @@ -24,8 +24,11 @@ import foundation.e.drive.services.OperationManagerService; * @author Vincent Bourgmayer */ public class ServiceExceptionHandler implements UncaughtExceptionHandler{ - private UncaughtExceptionHandler defaultUEH; private final static String TAG = ServiceExceptionHandler.class.getSimpleName(); + public final static String CRASH_LOG_FOLDER = "crash-logs"; + public final static String LOG_FILE_NAME_PREFIX = "eDrive-crash-"; + public final static String LOG_FILE_EXTENSION = ".log"; + private UncaughtExceptionHandler defaultUEH; Service service; @@ -58,9 +61,9 @@ public class ServiceExceptionHandler implements UncaughtExceptionHandler{ Long timestamp = System.currentTimeMillis(); //Create a new file that user can sent to us - String fileName = "eDrive-crash-"+timestamp+".log"; + String fileName = LOG_FILE_NAME_PREFIX+timestamp+LOG_FILE_EXTENSION; - File downloadDir = service.getApplication().getExternalFilesDir("Logs"); + File downloadDir = service.getApplication().getExternalFilesDir(CRASH_LOG_FOLDER); File logFile = new File(downloadDir, fileName); try { diff --git a/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java b/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java index c0b7086a..69950012 100644 --- a/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java +++ b/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java @@ -1,4 +1,48 @@ package foundation.e.drive.Test.FileFilterTest; +import org.junit.Assert; +import org.junit.Test; + public class CrashlogFileFilterTest { -} + + private String mockFileName(String target, String prefix, String extension){ + return prefix+target+extension; + } + + private String extractTimestamp(String fileName, String prefix, String extension){ + return fileName.substring(prefix.length(), (fileName.length() - extension.length())); + } + + @Test + public void extractTimeStampFromFileNameTest(){ + String prefix = "edrive-"; + String extension = ".log"; + String target = ""; + //Case 1 Empty Target + String base = mockFileName(target, prefix, extension); + Assert.assertEquals("Base length is incorrect", prefix.length()+extension.length(), base.length()); + + String fileTimestamp = extractTimestamp(base, prefix, extension); + Assert.assertEquals("result is not empty String", "", fileTimestamp); + + //Case 2: Prefix is empty + prefix = ""; + target = "1234"; + base = mockFileName(target, prefix, extension); + Assert.assertEquals("Base length is incorrect", prefix.length()+target.length()+extension.length(), base.length()); + + fileTimestamp = extractTimestamp(base, prefix, extension); + Assert.assertEquals("result is not empty String", target, fileTimestamp); + + //Case 3: extension is empty + + prefix = "edrive-"; + extension = ""; + + base = mockFileName(target, prefix, extension); + Assert.assertEquals("Base length is incorrect", prefix.length()+target.length(), base.length()); + + fileTimestamp = extractTimestamp(base, prefix, extension); + Assert.assertEquals("result is not empty String", target, fileTimestamp); + } +} \ No newline at end of file -- GitLab From 9ad7cb524faf715dd9b698beea19525e9ae07bd1 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Tue, 10 Dec 2019 16:48:18 +0100 Subject: [PATCH 09/16] add ServiceExceptionHandler into utils package --- .idea/codeStyles/Project.xml | 116 ++++++++++++++++++ .../drive/utils/ServiceExceptionHandler.java | 91 ++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..681f41ae --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java new file mode 100644 index 00000000..8c6dc4e3 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java @@ -0,0 +1,91 @@ +/* + * Copyright © Vincent Bourgmayer (/e/ foundation). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + */ +package foundation.e.drive.utils; +import android.app.Service; +import android.os.Environment; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.Thread.UncaughtExceptionHandler; + +/** + * @author Vincent Bourgmayer + */ +public class ServiceExceptionHandler implements UncaughtExceptionHandler{ + private UncaughtExceptionHandler defaultUEH; + + Service service; + + public ServiceExceptionHandler(Service service) { + this.service = service; + defaultUEH = Thread.getDefaultUncaughtExceptionHandler(); + } + + @Override + public void uncaughtException(Thread t, Throwable e) { + + if(isExternalStorageAvailable() && !isExternalStorageReadOnly()){ + //Get TimeStamp + Long timestamp = System.currentTimeMillis(); + + //Create a new file that user can sent to us + String fileName = "eDrive-crash-"+timestamp+".log"; + File downloadDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()); + File logFile = new File(downloadDir, fileName); + try { + FileOutputStream fos = new FileOutputStream(logFile); + fos.write(getStackTraceAsString(e).getBytes()); + fos.close(); + } catch (IOException exception) { + exception.printStackTrace(); + } + } + + //source: https://stackoverflow.com/questions/9050962/rethrow-uncaughtexceptionhandler-exception-after-logging-it/9050990#9050990 + if(defaultUEH != null){ + defaultUEH.uncaughtException(t, e); + }else{ + Log.d("ServiceExceptionHandler", "/e/ Drive has crashed and there is no ExceptionHandler"); + System.exit(1); //Kill /e/ Drive... + } + } + + //source: https://www.journaldev.com/9400/android-external-storage-read-write-save-file + private static boolean isExternalStorageAvailable() { + String extStorageState = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(extStorageState)) { + return true; + } + return false; + } + + //source: https://www.journaldev.com/9400/android-external-storage-read-write-save-file + private static boolean isExternalStorageReadOnly() { + String extStorageState = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState)) { + return true; + } + return false; + } + + /** + * Return the stackTrace of the exception as a String + * @param e the exception + * @return the Stacktrace as a string + */ + private String getStackTraceAsString(Throwable e){ + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + return sw.toString(); + } +} \ No newline at end of file -- GitLab From ea172591d0518112b65e98b0e6001e11e521af16 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Tue, 10 Dec 2019 17:41:29 +0100 Subject: [PATCH 10/16] use ServiceExceptionHandler in OMS, ObserverService & initializer --- .../e/drive/services/InitializerService.java | 2 ++ .../foundation/e/drive/services/ObserverService.java | 11 ++++------- .../e/drive/services/OperationManagerService.java | 3 +++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 546e5c09..8cedb2cc 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -37,6 +37,7 @@ import foundation.e.drive.receivers.ScreenOffReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.JobUtils; +import foundation.e.drive.utils.ServiceExceptionHandler; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; import static foundation.e.drive.utils.AppConstants.INITIALFOLDERS_NUMBER; @@ -67,6 +68,7 @@ public class InitializerService extends Service implements OnRemoteOperationList @Override public int onStartCommand( Intent intent, int flags, int startId ) { Log.i(TAG, "onStartCommand(...)"); + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); //Get account SharedPreferences prefs = this.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ); diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index 1afcb82a..ae84a86d 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -49,6 +49,8 @@ import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.DavClientProvider; import foundation.e.drive.utils.JobUtils; +import foundation.e.drive.utils.ServiceExceptionHandler; + import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; import static foundation.e.drive.utils.AppConstants.INITIALIZATION_HAS_BEEN_DONE; @@ -74,16 +76,11 @@ public class ObserverService extends Service implements OnRemoteOperationListene super.onDestroy(); this.mSyncedFolders = null; } - - @Override - public void onCreate() { - Log.i(TAG, "onCreate()"); - super.onCreate(); - } - + @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand("+startId+")"); + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); diff --git a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java index 786160bb..b0ee9313 100644 --- a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java +++ b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java @@ -40,6 +40,7 @@ import foundation.e.drive.operations.UploadFileOperation; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.DavClientProvider; +import foundation.e.drive.utils.ServiceExceptionHandler; /** * @author Vincent Bourgmayer @@ -247,6 +248,8 @@ public class OperationManagerService extends Service implements OnRemoteOperatio public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand()"); + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); + Bundle extras = intent.getExtras(); Log.d(TAG, "OperationManagerService recieved "+(extras == null ? "null extras": extras.size()+" operations to perform") ); -- GitLab From 518849027ee5e6c81633685efc81161742ec7394 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Wed, 11 Dec 2019 16:01:43 +0100 Subject: [PATCH 11/16] fix generation of multiple file --- .../e/drive/services/InitializerService.java | 7 ++++++- .../e/drive/services/ObserverService.java | 7 +++++-- .../drive/services/OperationManagerService.java | 16 +++++----------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 8cedb2cc..7289b42a 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -68,7 +68,12 @@ public class InitializerService extends Service implements OnRemoteOperationList @Override public int onStartCommand( Intent intent, int flags, int startId ) { Log.i(TAG, "onStartCommand(...)"); - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); + if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ + Log.d("ObserverService", "ServiceExceptionHandler already set!"); + }else{ + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); + } + //Get account SharedPreferences prefs = this.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ); diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index ae84a86d..f62c77cd 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -80,8 +80,11 @@ public class ObserverService extends Service implements OnRemoteOperationListene @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand("+startId+")"); - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); - + if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ + Log.d("ObserverService", "ServiceExceptionHandler already set!"); + }else{ + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); + } SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); diff --git a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java index b0ee9313..12e9268d 100644 --- a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java +++ b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java @@ -63,15 +63,6 @@ public class OperationManagerService extends Service implements OnRemoteOperatio @Override public void onDestroy() { Log.i(TAG, "onDestroy()"); - /*this.mOperationsQueue.clear(); - for(int i =-1, size = mThreadPool.length;++i < size;){ - try{ - if(mThreadPool[i] != null) - mThreadPool[i].interrupt();} - catch(Exception e){ - Log.e(TAG, e.toString()); - } - }*/ super.onDestroy(); } @@ -248,8 +239,11 @@ public class OperationManagerService extends Service implements OnRemoteOperatio public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand()"); - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); - + if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ + Log.d("ObserverService", "ServiceExceptionHandler already set!"); + }else{ + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); + } Bundle extras = intent.getExtras(); Log.d(TAG, "OperationManagerService recieved "+(extras == null ? "null extras": extras.size()+" operations to perform") ); -- GitLab From bff139f659d2684e8a804b33b59d014fe3b580bb Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Fri, 13 Dec 2019 09:00:01 +0100 Subject: [PATCH 12/16] add method in CommonUtils to set the ServiceUncaughtExceptionHandler --- .../foundation/e/drive/utils/CommonUtils.java | 16 ++++++++++++++++ .../e/drive/utils/ServiceExceptionHandler.java | 10 ++++++++++ 2 files changed, 26 insertions(+) diff --git a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java index 421a58bc..398a16e5 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -12,6 +12,7 @@ package foundation.e.drive.utils; import android.accounts.Account; import android.accounts.AccountManager; +import android.app.Service; import android.content.ContentResolver; import android.content.Context; import android.media.MediaScannerConnection; @@ -39,6 +40,21 @@ import static foundation.e.drive.utils.AppConstants.SETTINGSYNC_PROVIDER_AUTHORI public abstract class CommonUtils { final private static String TAG = CommonUtils.class.getSimpleName(); + /** + * Set ServiceUncaughtExceptionHandler to be the MainThread Exception Handler + * Or update the service which use it + * @param service current service + */ + public static void setServiceUnCaughtExceptionHandler(Service service){ + + Thread.UncaughtExceptionHandler defaultUEH = Thread.getDefaultUncaughtExceptionHandler(); + if(defaultUEH.getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ + Log.d("ObserverService", "ServiceExceptionHandler already set!"); + ((ServiceExceptionHandler) defaultUEH).setService(service); + }else{ + Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(service)); + } + } /** * Unregister from screeOffReceiver component diff --git a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java index 8c6dc4e3..b790e6f8 100644 --- a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java +++ b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java @@ -23,8 +23,18 @@ import java.lang.Thread.UncaughtExceptionHandler; public class ServiceExceptionHandler implements UncaughtExceptionHandler{ private UncaughtExceptionHandler defaultUEH; + + Service service; + /** + * Update the service which use this handler + * @param service current running service + */ + public void setService(Service service) { + this.service = service; + } + public ServiceExceptionHandler(Service service) { this.service = service; defaultUEH = Thread.getDefaultUncaughtExceptionHandler(); -- GitLab From 8d53d57d1799e5061780c5ccc39f1059047e774c Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Fri, 13 Dec 2019 09:03:12 +0100 Subject: [PATCH 13/16] Check in ServiceUEH if service is OMS and then disable OMS_IS_WORKING --- .../drive/utils/ServiceExceptionHandler.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java index b790e6f8..9bdfa088 100644 --- a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java +++ b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java @@ -7,6 +7,7 @@ */ package foundation.e.drive.utils; import android.app.Service; +import android.content.Context; import android.os.Environment; import android.util.Log; @@ -17,13 +18,14 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.lang.Thread.UncaughtExceptionHandler; +import foundation.e.drive.services.OperationManagerService; + /** * @author Vincent Bourgmayer */ public class ServiceExceptionHandler implements UncaughtExceptionHandler{ private UncaughtExceptionHandler defaultUEH; - - + private final static String TAG = ServiceExceptionHandler.class.getSimpleName(); Service service; @@ -42,6 +44,14 @@ public class ServiceExceptionHandler implements UncaughtExceptionHandler{ @Override public void uncaughtException(Thread t, Throwable e) { + Log.d(TAG, "Service class: "+service.getClass().getSimpleName()); + //IF OMS is crashing, set settings that it runs to false; + if(service.getClass().getSimpleName().equals(OperationManagerService.class.getSimpleName())){ + service.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) + .edit() + .putBoolean(AppConstants.KEY_OMS_IS_WORKING, false) + .apply(); + } if(isExternalStorageAvailable() && !isExternalStorageReadOnly()){ //Get TimeStamp @@ -49,22 +59,26 @@ public class ServiceExceptionHandler implements UncaughtExceptionHandler{ //Create a new file that user can sent to us String fileName = "eDrive-crash-"+timestamp+".log"; - File downloadDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()); + + File downloadDir = service.getApplication().getExternalFilesDir("Logs"); File logFile = new File(downloadDir, fileName); try { + FileOutputStream fos = new FileOutputStream(logFile); + fos.write(service.getClass().getSimpleName().getBytes()); fos.write(getStackTraceAsString(e).getBytes()); fos.close(); + logFile.setReadable(true, false); + } catch (IOException exception) { exception.printStackTrace(); } } - //source: https://stackoverflow.com/questions/9050962/rethrow-uncaughtexceptionhandler-exception-after-logging-it/9050990#9050990 if(defaultUEH != null){ defaultUEH.uncaughtException(t, e); }else{ - Log.d("ServiceExceptionHandler", "/e/ Drive has crashed and there is no ExceptionHandler"); + Log.d(TAG, "/e/ Drive has crashed and there is no ExceptionHandler"); System.exit(1); //Kill /e/ Drive... } } -- GitLab From 2243c0f44aa72a4bf1656a163e07e7784c9b70b8 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Fri, 13 Dec 2019 09:05:19 +0100 Subject: [PATCH 14/16] use CommonUtils.setServiceUncaughtException(Service) in concerned service --- .../foundation/e/drive/services/InitializerService.java | 6 +----- .../java/foundation/e/drive/services/ObserverService.java | 8 +++----- .../e/drive/services/OperationManagerService.java | 7 ++----- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 7289b42a..607edf9d 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -68,11 +68,7 @@ public class InitializerService extends Service implements OnRemoteOperationList @Override public int onStartCommand( Intent intent, int flags, int startId ) { Log.i(TAG, "onStartCommand(...)"); - if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ - Log.d("ObserverService", "ServiceExceptionHandler already set!"); - }else{ - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); - } + CommonUtils.setServiceUnCaughtExceptionHandler(this); //Get account SharedPreferences prefs = this.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ); diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index f62c77cd..187563b8 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -80,11 +80,9 @@ public class ObserverService extends Service implements OnRemoteOperationListene @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand("+startId+")"); - if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ - Log.d("ObserverService", "ServiceExceptionHandler already set!"); - }else{ - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); - } + + CommonUtils.setServiceUnCaughtExceptionHandler(this); + SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); diff --git a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java index 12e9268d..eb0999c2 100644 --- a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java +++ b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java @@ -239,11 +239,8 @@ public class OperationManagerService extends Service implements OnRemoteOperatio public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand()"); - if(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName().equals(ServiceExceptionHandler.class.getSimpleName())){ - Log.d("ObserverService", "ServiceExceptionHandler already set!"); - }else{ - Thread.setDefaultUncaughtExceptionHandler(new ServiceExceptionHandler(this)); - } + CommonUtils.setServiceUnCaughtExceptionHandler(this); + Bundle extras = intent.getExtras(); Log.d(TAG, "OperationManagerService recieved "+(extras == null ? "null extras": extras.size()+" operations to perform") ); -- GitLab From 05d8a901beb511e27c920aff4b36e47ab2acce97 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Sat, 14 Dec 2019 15:25:01 +0100 Subject: [PATCH 15/16] add method to remove oldestCrashlogs. add CrashlogsFileFilter with unitTest class. --- .../fileFilters/CrashlogsFileFilter.java | 4 +++ .../e/drive/services/ObserverService.java | 25 +++++++++++++++++++ .../CrashlogFileFilterTest.java | 4 +++ 3 files changed, 33 insertions(+) create mode 100644 app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java create mode 100644 app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java diff --git a/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java b/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java new file mode 100644 index 00000000..791312e1 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java @@ -0,0 +1,4 @@ +package foundation.e.drive.fileFilters; + +public class CrashlogsFileFilter { +} diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index 187563b8..bca4232f 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -37,6 +37,7 @@ import java.util.ListIterator; import java.util.Map; import foundation.e.drive.database.DbHelper; +import foundation.e.drive.fileFilters.CrashlogsFileFilter; import foundation.e.drive.fileFilters.FileFilterFactory; import foundation.e.drive.fileFilters.OnlyFileFilter; import foundation.e.drive.models.SyncedFolder; @@ -151,12 +152,36 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.i(TAG, "begin()"); this.isWorking = true; clearCachedFile(); + deleteOldestCrashlogs(); startScan(true); } + /** + * This method remove all the crash-logs file + * in external dir that are 10 days or more old. + */ + private void deleteOldestCrashlogs(){ + Log.i(TAG, "deleteOldestCrashLogs()"); + File[] fileToRemove = getExternalFilesDir(ServiceExceptionHandler.CRASH_LOG_FOLDER) + .listFiles(new CrashlogsFileFilter()); + + int counter = 0; + for (File file : fileToRemove) { + try { + file.delete(); + ++counter; + }catch (SecurityException e){ + e.printStackTrace(); + } + } + Log.d(TAG, counter+" old crashlogs file.s deleted"); + } + + /** * Clear cached file unused: * remove each cached file which isn't in OperationManagerService.lockedSyncedFileState(); + * @TODO rewrite this method! */ private void clearCachedFile(){ Log.i(TAG, "clearCachedFile()"); diff --git a/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java b/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java new file mode 100644 index 00000000..c0b7086a --- /dev/null +++ b/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java @@ -0,0 +1,4 @@ +package foundation.e.drive.Test.FileFilterTest; + +public class CrashlogFileFilterTest { +} -- GitLab From 6f11387dec68e9c4dadab3b7186bc5d78e878195 Mon Sep 17 00:00:00 2001 From: vince-bourgmayer Date: Sat, 14 Dec 2019 15:26:44 +0100 Subject: [PATCH 16/16] add method to remove oldestCrashlogs. add CrashlogsFileFilter with unitTest class. --- .../fileFilters/CrashlogsFileFilter.java | 42 ++++++++++++++++- .../drive/utils/ServiceExceptionHandler.java | 9 ++-- .../CrashlogFileFilterTest.java | 46 ++++++++++++++++++- 3 files changed, 91 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java b/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java index 791312e1..5a249733 100644 --- a/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java +++ b/app/src/main/java/foundation/e/drive/fileFilters/CrashlogsFileFilter.java @@ -1,4 +1,42 @@ package foundation.e.drive.fileFilters; -public class CrashlogsFileFilter { -} +import java.io.File; +import java.io.FileFilter; + +import foundation.e.drive.utils.ServiceExceptionHandler; + +public class CrashlogsFileFilter implements FileFilter { + private final static long max_timestamp_delta = 864000000; //10 days in ms (240*3600*1000) + + @Override + public boolean accept(File pathname) { + String fileTimestamp = extractTimestamp(pathname.getName(), + ServiceExceptionHandler.LOG_FILE_NAME_PREFIX, + ServiceExceptionHandler.LOG_FILE_EXTENSION); + + long timestamp; + try { + timestamp = Long.parseLong(fileTimestamp); + }catch (NumberFormatException e){ + //Can't parse the extracted timestamp + //This file has not the expected name. It must be removed + return true; + } + + //if current Date - file date >= max deta allowed + return ((System.currentTimeMillis() - timestamp ) >= max_timestamp_delta); + } + + /** + * Extract the timestamp from the name of the file + * UnitTested! + * @param fileName Filename + * @param prefix prefix to ignore + * @param extension extension to ignore + * @return the timestamp extracted from the name + */ + private String extractTimestamp(String fileName, String prefix, String extension){ + return fileName.substring(prefix.length(), (fileName.length() - extension.length())); + } + +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java index 9bdfa088..250e791b 100644 --- a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java +++ b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java @@ -24,8 +24,11 @@ import foundation.e.drive.services.OperationManagerService; * @author Vincent Bourgmayer */ public class ServiceExceptionHandler implements UncaughtExceptionHandler{ - private UncaughtExceptionHandler defaultUEH; private final static String TAG = ServiceExceptionHandler.class.getSimpleName(); + public final static String CRASH_LOG_FOLDER = "crash-logs"; + public final static String LOG_FILE_NAME_PREFIX = "eDrive-crash-"; + public final static String LOG_FILE_EXTENSION = ".log"; + private UncaughtExceptionHandler defaultUEH; Service service; @@ -58,9 +61,9 @@ public class ServiceExceptionHandler implements UncaughtExceptionHandler{ Long timestamp = System.currentTimeMillis(); //Create a new file that user can sent to us - String fileName = "eDrive-crash-"+timestamp+".log"; + String fileName = LOG_FILE_NAME_PREFIX+timestamp+LOG_FILE_EXTENSION; - File downloadDir = service.getApplication().getExternalFilesDir("Logs"); + File downloadDir = service.getApplication().getExternalFilesDir(CRASH_LOG_FOLDER); File logFile = new File(downloadDir, fileName); try { diff --git a/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java b/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java index c0b7086a..69950012 100644 --- a/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java +++ b/app/src/test/java/foundation/e/drive/Test/FileFilterTest/CrashlogFileFilterTest.java @@ -1,4 +1,48 @@ package foundation.e.drive.Test.FileFilterTest; +import org.junit.Assert; +import org.junit.Test; + public class CrashlogFileFilterTest { -} + + private String mockFileName(String target, String prefix, String extension){ + return prefix+target+extension; + } + + private String extractTimestamp(String fileName, String prefix, String extension){ + return fileName.substring(prefix.length(), (fileName.length() - extension.length())); + } + + @Test + public void extractTimeStampFromFileNameTest(){ + String prefix = "edrive-"; + String extension = ".log"; + String target = ""; + //Case 1 Empty Target + String base = mockFileName(target, prefix, extension); + Assert.assertEquals("Base length is incorrect", prefix.length()+extension.length(), base.length()); + + String fileTimestamp = extractTimestamp(base, prefix, extension); + Assert.assertEquals("result is not empty String", "", fileTimestamp); + + //Case 2: Prefix is empty + prefix = ""; + target = "1234"; + base = mockFileName(target, prefix, extension); + Assert.assertEquals("Base length is incorrect", prefix.length()+target.length()+extension.length(), base.length()); + + fileTimestamp = extractTimestamp(base, prefix, extension); + Assert.assertEquals("result is not empty String", target, fileTimestamp); + + //Case 3: extension is empty + + prefix = "edrive-"; + extension = ""; + + base = mockFileName(target, prefix, extension); + Assert.assertEquals("Base length is incorrect", prefix.length()+target.length(), base.length()); + + fileTimestamp = extractTimestamp(base, prefix, extension); + Assert.assertEquals("result is not empty String", target, fileTimestamp); + } +} \ No newline at end of file -- GitLab