From a519f78ccd3caca0a1ea8ead072c840b595cfd92 Mon Sep 17 00:00:00 2001 From: f43nd1r Date: Wed, 18 Apr 2018 20:14:59 +0200 Subject: [PATCH 1/5] only approve one report at a time, instead of all of them. Allow replacing default scheduler. --- .../java/org/acra/builder/ReportExecutor.java | 18 +++--- .../scheduler/DefaultSenderScheduler.java | 26 +++++++++ .../org/acra/scheduler/SchedulerStarter.java | 57 +++++++++++++++++++ .../org/acra/scheduler/SenderScheduler.java | 14 +++++ .../java/org/acra/sender/SenderService.java | 21 ------- .../org/acra/sender/SenderServiceStarter.java | 56 ------------------ .../util/ApplicationStartupProcessor.java | 11 +--- .../acra/dialog/BaseCrashReportDialog.java | 6 +- .../NotificationBroadcastReceiver.java | 7 +-- 9 files changed, 112 insertions(+), 104 deletions(-) create mode 100644 acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java create mode 100644 acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java create mode 100644 acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java delete mode 100644 acra-core/src/main/java/org/acra/sender/SenderServiceStarter.java diff --git a/acra-core/src/main/java/org/acra/builder/ReportExecutor.java b/acra-core/src/main/java/org/acra/builder/ReportExecutor.java index 36be1834a7..41be918bd0 100644 --- a/acra-core/src/main/java/org/acra/builder/ReportExecutor.java +++ b/acra-core/src/main/java/org/acra/builder/ReportExecutor.java @@ -22,7 +22,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.widget.Toast; - import org.acra.ACRA; import org.acra.ACRAConstants; import org.acra.config.CoreConfiguration; @@ -32,13 +31,13 @@ import org.acra.file.CrashReportPersister; import org.acra.file.ReportLocator; import org.acra.interaction.ReportInteractionExecutor; -import org.acra.sender.SenderServiceStarter; import org.acra.plugins.PluginLoader; +import org.acra.scheduler.SchedulerStarter; import org.acra.util.ProcessFinisher; import org.acra.util.ToastSender; import java.io.File; -import java.util.*; +import java.util.List; import static org.acra.ACRA.LOG_TAG; import static org.acra.ReportField.IS_SILENT; @@ -157,15 +156,15 @@ public final void execute(@NonNull final ReportBuilder reportBuilder) { saveCrashReportFile(reportFile, crashReportData); final ReportInteractionExecutor executor = new ReportInteractionExecutor(context, config); - StrictMode.setThreadPolicy(oldPolicy); if (reportBuilder.isSendSilently()) { - //if size == 0 we have no interaction and can send all reports - startSendingReports(executor.hasInteractions()); + //if no interactions are present we can send all reports + sendReport(reportFile, executor.hasInteractions()); } else { if (executor.performInteractions(reportFile)) { - startSendingReports(false); + sendReport(reportFile, false); } } + StrictMode.setThreadPolicy(oldPolicy); } else { if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Not sending crash report because of ReportingAdministrator " + blockingAdministrator.getClass().getName()); try { @@ -225,10 +224,9 @@ private void endApplication(@Nullable Thread uncaughtExceptionThread, Throwable * * @param onlySendSilentReports If true then only send silent reports. */ - private void startSendingReports(boolean onlySendSilentReports) { + private void sendReport(@NonNull File report, boolean onlySendSilentReports) { if (enabled) { - final SenderServiceStarter starter = new SenderServiceStarter(context, config); - starter.startService(onlySendSilentReports, true); + new SchedulerStarter(context, config).scheduleReports(report, onlySendSilentReports); } else { ACRA.log.w(LOG_TAG, "Would be sending reports, but ACRA is disabled"); } diff --git a/acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java b/acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java new file mode 100644 index 0000000000..66975d1bee --- /dev/null +++ b/acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java @@ -0,0 +1,26 @@ +package org.acra.scheduler; + +import android.content.Context; +import android.content.Intent; +import android.support.annotation.NonNull; +import android.support.v4.app.JobIntentService; +import org.acra.ACRA; +import org.acra.config.CoreConfiguration; +import org.acra.sender.SenderService; + +import static org.acra.ACRA.LOG_TAG; + +/** + * @author F43nd1r + * @since 18.04.18 + */ +public class DefaultSenderScheduler implements SenderScheduler { + @Override + public void scheduleReportSending(@NonNull Context context, @NonNull CoreConfiguration config, boolean onlySendSilentReports) { + if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "About to start SenderService"); + final Intent intent = new Intent(); + intent.putExtra(SenderService.EXTRA_ONLY_SEND_SILENT_REPORTS, onlySendSilentReports); + intent.putExtra(SenderService.EXTRA_ACRA_CONFIG, config); + JobIntentService.enqueueWork(context, SenderService.class, 0, intent); + } +} diff --git a/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java b/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java new file mode 100644 index 0000000000..25209ce119 --- /dev/null +++ b/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java @@ -0,0 +1,57 @@ +package org.acra.scheduler; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import org.acra.ACRA; +import org.acra.config.CoreConfiguration; +import org.acra.file.ReportLocator; +import org.acra.plugins.PluginLoader; + +import java.io.File; +import java.util.List; + +import static org.acra.ACRA.LOG_TAG; + +/** + * @author F43nd1r + * @since 18.04.18 + */ +public class SchedulerStarter { + + private final Context context; + private final CoreConfiguration config; + private final ReportLocator locator; + private final SenderScheduler senderScheduler; + + public SchedulerStarter(@NonNull Context context, @NonNull CoreConfiguration config) { + this.context = context; + this.config = config; + locator = new ReportLocator(context); + List schedulers = new PluginLoader(config).load(SenderScheduler.class); + if (schedulers.isEmpty()) { + senderScheduler = new DefaultSenderScheduler(); + } else { + senderScheduler = schedulers.get(0); + if (schedulers.size() > 1) ACRA.log.w(ACRA.LOG_TAG, "More than one SenderScheduler found. Will use only " + senderScheduler.getClass().getSimpleName()); + } + } + + /** + * Starts a process to start sending outstanding error reports. + * + * @param report If not null, this report will be approved before scheduling. + * @param onlySendSilentReports If true then only send silent reports. + */ + public void scheduleReports(@Nullable File report, boolean onlySendSilentReports) { + if (report != null) { + if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Mark " + report.getName() + " as approved."); + final File approvedReport = new File(locator.getApprovedFolder(), report.getName()); + if (!report.renameTo(approvedReport)) { + ACRA.log.w(LOG_TAG, "Could not rename approved report from " + report + " to " + approvedReport); + } + } + if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Schedule report sending"); + senderScheduler.scheduleReportSending(context, config, onlySendSilentReports); + } +} diff --git a/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java b/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java new file mode 100644 index 0000000000..fdfb7da2ae --- /dev/null +++ b/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java @@ -0,0 +1,14 @@ +package org.acra.scheduler; + +import android.content.Context; +import android.support.annotation.NonNull; +import org.acra.config.CoreConfiguration; +import org.acra.plugins.Plugin; + +/** + * @author F43nd1r + * @since 18.04.18 + */ +public interface SenderScheduler extends Plugin { + void scheduleReportSending(@NonNull Context context, @NonNull CoreConfiguration config, boolean onlySendSilentReports); +} diff --git a/acra-core/src/main/java/org/acra/sender/SenderService.java b/acra-core/src/main/java/org/acra/sender/SenderService.java index f6f202b15f..dba2189ae2 100644 --- a/acra-core/src/main/java/org/acra/sender/SenderService.java +++ b/acra-core/src/main/java/org/acra/sender/SenderService.java @@ -39,7 +39,6 @@ public class SenderService extends JobIntentService { public static final String EXTRA_ONLY_SEND_SILENT_REPORTS = "onlySendSilentReports"; - public static final String EXTRA_APPROVE_REPORTS_FIRST = "approveReportsFirst"; public static final String EXTRA_ACRA_CONFIG = "acraConfig"; private final ReportLocator locator; @@ -56,7 +55,6 @@ protected void onHandleWork(@NonNull Intent intent) { } final boolean onlySendSilentReports = intent.getBooleanExtra(EXTRA_ONLY_SEND_SILENT_REPORTS, false); - final boolean approveReportsFirst = intent.getBooleanExtra(EXTRA_APPROVE_REPORTS_FIRST, false); final CoreConfiguration config = (CoreConfiguration) intent.getSerializableExtra(EXTRA_ACRA_CONFIG); @@ -64,11 +62,6 @@ protected void onHandleWork(@NonNull Intent intent) { try { final List senderInstances = getSenderInstances(config); - // Mark reports as approved - if (approveReportsFirst) { - markReportsAsApproved(); - } - // Get approved reports final File[] reports = locator.getApprovedReports(); @@ -115,18 +108,4 @@ private List getSenderInstances(@NonNull CoreConfiguration config) if (reportSenders.isEmpty()) reportSenders.add(new NullSender()); return reportSenders; } - - /** - * Flag all pending reports as "approved" by the user. These reports can be sent. - */ - private void markReportsAsApproved() { - if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Mark all pending reports as approved."); - - for (File report : locator.getUnapprovedReports()) { - final File approvedReport = new File(locator.getApprovedFolder(), report.getName()); - if (!report.renameTo(approvedReport)) { - ACRA.log.w(LOG_TAG, "Could not rename approved report from " + report + " to " + approvedReport); - } - } - } } diff --git a/acra-core/src/main/java/org/acra/sender/SenderServiceStarter.java b/acra-core/src/main/java/org/acra/sender/SenderServiceStarter.java deleted file mode 100644 index 1ab3bb59fa..0000000000 --- a/acra-core/src/main/java/org/acra/sender/SenderServiceStarter.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2017 the ACRA team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.acra.sender; - -import android.content.Context; -import android.content.Intent; -import android.support.annotation.NonNull; -import android.support.v4.app.JobIntentService; - -import org.acra.ACRA; -import org.acra.config.CoreConfiguration; - -import static org.acra.ACRA.LOG_TAG; - -/** - * Starts the Service(Intent)Service to process and send pending reports. - */ -public class SenderServiceStarter { - - private final Context context; - private final CoreConfiguration config; - - public SenderServiceStarter(@NonNull Context context, @NonNull CoreConfiguration config) { - this.context = context; - this.config = config; - } - - /** - * Starts a process to start sending outstanding error reports. - * - * @param onlySendSilentReports If true then only send silent reports. - * @param approveReportsFirst If true then approve unapproved reports first. - */ - public void startService(boolean onlySendSilentReports, boolean approveReportsFirst) { - if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "About to start SenderService"); - final Intent intent = new Intent(); - intent.putExtra(SenderService.EXTRA_ONLY_SEND_SILENT_REPORTS, onlySendSilentReports); - intent.putExtra(SenderService.EXTRA_APPROVE_REPORTS_FIRST, approveReportsFirst); - intent.putExtra(SenderService.EXTRA_ACRA_CONFIG, config); - JobIntentService.enqueueWork(context, SenderService.class, 0, intent); - } -} diff --git a/acra-core/src/main/java/org/acra/util/ApplicationStartupProcessor.java b/acra-core/src/main/java/org/acra/util/ApplicationStartupProcessor.java index 449d5be78d..c798c0933b 100644 --- a/acra-core/src/main/java/org/acra/util/ApplicationStartupProcessor.java +++ b/acra-core/src/main/java/org/acra/util/ApplicationStartupProcessor.java @@ -21,22 +21,17 @@ import android.content.pm.PackageInfo; import android.os.Handler; import android.support.annotation.NonNull; - import org.acra.ACRA; -import org.acra.ACRAConstants; import org.acra.config.CoreConfiguration; import org.acra.file.BulkReportDeleter; import org.acra.file.CrashReportFileNameParser; import org.acra.file.ReportLocator; import org.acra.interaction.ReportInteractionExecutor; import org.acra.prefs.SharedPreferencesFactory; -import org.acra.sender.SenderServiceStarter; +import org.acra.scheduler.SchedulerStarter; import java.io.File; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Calendar; -import java.util.Locale; /** * Looks for any existing reports and starts sending them. @@ -113,9 +108,7 @@ private void sendApprovedReports() { } // Send the approved reports. - final SenderServiceStarter starter = new SenderServiceStarter(context, config); - starter.startService(false, false); - + new SchedulerStarter(context, config).scheduleReports(null, false); } /** diff --git a/acra-dialog/src/main/java/org/acra/dialog/BaseCrashReportDialog.java b/acra-dialog/src/main/java/org/acra/dialog/BaseCrashReportDialog.java index af9b4b3c7d..ebd8b9b762 100644 --- a/acra-dialog/src/main/java/org/acra/dialog/BaseCrashReportDialog.java +++ b/acra-dialog/src/main/java/org/acra/dialog/BaseCrashReportDialog.java @@ -19,14 +19,13 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; - import org.acra.ACRA; import org.acra.config.CoreConfiguration; import org.acra.data.CrashReportData; import org.acra.file.BulkReportDeleter; import org.acra.file.CrashReportPersister; import org.acra.interaction.DialogInteraction; -import org.acra.sender.SenderServiceStarter; +import org.acra.scheduler.SchedulerStarter; import org.json.JSONException; import java.io.File; @@ -134,8 +133,7 @@ protected final void sendCrash(@Nullable String comment, @Nullable String userEm } // Start the report sending task - final SenderServiceStarter starter = new SenderServiceStarter(this, config); - starter.startService(false, true); + new SchedulerStarter(this, config).scheduleReports(reportFile, false); }).start(); } diff --git a/acra-notification/src/main/java/org/acra/receiver/NotificationBroadcastReceiver.java b/acra-notification/src/main/java/org/acra/receiver/NotificationBroadcastReceiver.java index a3f335300a..2b496ef898 100644 --- a/acra-notification/src/main/java/org/acra/receiver/NotificationBroadcastReceiver.java +++ b/acra-notification/src/main/java/org/acra/receiver/NotificationBroadcastReceiver.java @@ -23,15 +23,14 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.RemoteInput; - import org.acra.ACRA; -import org.acra.data.CrashReportData; import org.acra.config.CoreConfiguration; +import org.acra.data.CrashReportData; import org.acra.file.BulkReportDeleter; import org.acra.file.CrashReportPersister; import org.acra.interaction.NotificationInteraction; +import org.acra.scheduler.SchedulerStarter; import org.acra.sender.SenderService; -import org.acra.sender.SenderServiceStarter; import org.acra.util.SystemServices; import org.json.JSONException; @@ -76,7 +75,7 @@ public void onReceive(@NonNull Context context, @NonNull Intent intent) { } } } - new SenderServiceStarter(context, config).startService(false, true); + new SchedulerStarter(context, config).scheduleReports(reportFile, false); } break; case NotificationInteraction.INTENT_ACTION_DISCARD: From aa9ff8c84caf59175c28fb60aa58f6ad4d3d0f94 Mon Sep 17 00:00:00 2001 From: f43nd1r Date: Wed, 18 Apr 2018 20:28:23 +0200 Subject: [PATCH 2/5] fix copyright --- .../org/acra/plugins/AllowsDisablePlugin.java | 16 ++++++++++++++++ .../plugins/ConfigBasedAllowsDisablePlugin.java | 16 ++++++++++++++++ .../src/main/java/org/acra/plugins/Plugin.java | 16 ++++++++++++++++ .../main/java/org/acra/plugins/PluginLoader.java | 16 ++++++++++++++++ .../acra/scheduler/DefaultSenderScheduler.java | 16 ++++++++++++++++ .../org/acra/scheduler/SchedulerStarter.java | 16 ++++++++++++++++ .../java/org/acra/scheduler/SenderScheduler.java | 16 ++++++++++++++++ 7 files changed, 112 insertions(+) diff --git a/acra-core/src/main/java/org/acra/plugins/AllowsDisablePlugin.java b/acra-core/src/main/java/org/acra/plugins/AllowsDisablePlugin.java index 0d0f6531ab..dee2208c4e 100644 --- a/acra-core/src/main/java/org/acra/plugins/AllowsDisablePlugin.java +++ b/acra-core/src/main/java/org/acra/plugins/AllowsDisablePlugin.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.acra.plugins; import android.support.annotation.NonNull; diff --git a/acra-core/src/main/java/org/acra/plugins/ConfigBasedAllowsDisablePlugin.java b/acra-core/src/main/java/org/acra/plugins/ConfigBasedAllowsDisablePlugin.java index bad11e1491..7dd838f588 100644 --- a/acra-core/src/main/java/org/acra/plugins/ConfigBasedAllowsDisablePlugin.java +++ b/acra-core/src/main/java/org/acra/plugins/ConfigBasedAllowsDisablePlugin.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.acra.plugins; import android.support.annotation.NonNull; diff --git a/acra-core/src/main/java/org/acra/plugins/Plugin.java b/acra-core/src/main/java/org/acra/plugins/Plugin.java index d5045aad1d..2863760c52 100644 --- a/acra-core/src/main/java/org/acra/plugins/Plugin.java +++ b/acra-core/src/main/java/org/acra/plugins/Plugin.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.acra.plugins; /** diff --git a/acra-core/src/main/java/org/acra/plugins/PluginLoader.java b/acra-core/src/main/java/org/acra/plugins/PluginLoader.java index 38cc69ed3f..a7862c70f2 100644 --- a/acra-core/src/main/java/org/acra/plugins/PluginLoader.java +++ b/acra-core/src/main/java/org/acra/plugins/PluginLoader.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.acra.plugins; import android.support.annotation.NonNull; diff --git a/acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java b/acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java index 66975d1bee..3ee6feaee2 100644 --- a/acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java +++ b/acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.acra.scheduler; import android.content.Context; diff --git a/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java b/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java index 25209ce119..8a634c9de8 100644 --- a/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java +++ b/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.acra.scheduler; import android.content.Context; diff --git a/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java b/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java index fdfb7da2ae..1b6d4cd8e5 100644 --- a/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java +++ b/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.acra.scheduler; import android.content.Context; From 84307388da268f4fecf52660ef77f4be17b4b4ed Mon Sep 17 00:00:00 2001 From: f43nd1r Date: Wed, 18 Apr 2018 21:45:31 +0200 Subject: [PATCH 3/5] introduce advanced scheduling --- acra-advanced-scheduler/build.gradle | 27 +++++++ .../src/main/AndroidManifest.xml | 17 ++++ .../org/acra/annotation/AcraScheduler.java | 37 +++++++++ .../scheduler/AdvancedSenderScheduler.java | 77 +++++++++++++++++++ .../java/org/acra/scheduler/ReportJob.java | 53 +++++++++++++ .../org/acra/scheduler/ReportJobCreator.java | 37 +++++++++ acra-core/src/main/java/org/acra/ACRA.java | 6 +- .../java/org/acra/builder/ReportExecutor.java | 6 +- .../org/acra/reporter/ErrorReporterImpl.java | 5 +- .../org/acra/scheduler/SchedulerStarter.java | 1 + .../org/acra/scheduler/SenderScheduler.java | 3 + .../util/ApplicationStartupProcessor.java | 6 +- gradle.properties | 3 +- settings.gradle | 2 +- 14 files changed, 270 insertions(+), 10 deletions(-) create mode 100644 acra-advanced-scheduler/build.gradle create mode 100644 acra-advanced-scheduler/src/main/AndroidManifest.xml create mode 100644 acra-advanced-scheduler/src/main/java/org/acra/annotation/AcraScheduler.java create mode 100644 acra-advanced-scheduler/src/main/java/org/acra/scheduler/AdvancedSenderScheduler.java create mode 100644 acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJob.java create mode 100644 acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJobCreator.java diff --git a/acra-advanced-scheduler/build.gradle b/acra-advanced-scheduler/build.gradle new file mode 100644 index 0000000000..07911b0f9b --- /dev/null +++ b/acra-advanced-scheduler/build.gradle @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'com.android.library' +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' + +dependencies { + api project(':acra-core') + implementation "com.evernote:android-job:$jobVersion" + compileOnly "com.google.auto.service:auto-service:$autoServiceVersion" + annotationProcessor project(':annotationprocessor') + compileOnly project(':annotations') +} \ No newline at end of file diff --git a/acra-advanced-scheduler/src/main/AndroidManifest.xml b/acra-advanced-scheduler/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..9af0a4fc5c --- /dev/null +++ b/acra-advanced-scheduler/src/main/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + diff --git a/acra-advanced-scheduler/src/main/java/org/acra/annotation/AcraScheduler.java b/acra-advanced-scheduler/src/main/java/org/acra/annotation/AcraScheduler.java new file mode 100644 index 0000000000..e34bc223d1 --- /dev/null +++ b/acra-advanced-scheduler/src/main/java/org/acra/annotation/AcraScheduler.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acra.annotation; + +import com.evernote.android.job.JobRequest; + +import java.lang.annotation.*; + +/** + * @author F43nd1r + * @since 18.04.18 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +@Configuration +public @interface AcraScheduler { + JobRequest.NetworkType requiresNetworkType() default JobRequest.NetworkType.ANY; + boolean requiresCharging() default false; + boolean requiresDeviceIdle() default false; + boolean requiresBatteryNotLow() default false; +} diff --git a/acra-advanced-scheduler/src/main/java/org/acra/scheduler/AdvancedSenderScheduler.java b/acra-advanced-scheduler/src/main/java/org/acra/scheduler/AdvancedSenderScheduler.java new file mode 100644 index 0000000000..bf55290222 --- /dev/null +++ b/acra-advanced-scheduler/src/main/java/org/acra/scheduler/AdvancedSenderScheduler.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acra.scheduler; + +import android.content.Context; +import android.support.annotation.NonNull; +import com.evernote.android.job.JobManager; +import com.evernote.android.job.JobRequest; +import com.evernote.android.job.util.support.PersistableBundleCompat; +import com.google.auto.service.AutoService; +import org.acra.ACRA; +import org.acra.config.ConfigUtils; +import org.acra.config.CoreConfiguration; +import org.acra.config.SchedulerConfiguration; +import org.acra.plugins.ConfigBasedAllowsDisablePlugin; +import org.acra.sender.SenderService; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.concurrent.TimeUnit; + +/** + * @author F43nd1r + * @since 18.04.18 + */ +@AutoService(SenderScheduler.class) +public class AdvancedSenderScheduler extends ConfigBasedAllowsDisablePlugin implements SenderScheduler { + + public AdvancedSenderScheduler() { + super(SchedulerConfiguration.class); + } + + @Override + public void scheduleReportSending(@NonNull Context context, @NonNull CoreConfiguration config, boolean onlySendSilentReports) { + SchedulerConfiguration schedulerConfiguration = ConfigUtils.getPluginConfiguration(config, SchedulerConfiguration.class); + PersistableBundleCompat extras = new PersistableBundleCompat(); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try(ObjectOutputStream out = new ObjectOutputStream(bytes)){ + out.writeObject(config); + } catch (IOException e) { + ACRA.log.w("Failed to write config to string", e); + } + extras.putString(SenderService.EXTRA_ACRA_CONFIG, android.util.Base64.encodeToString(bytes.toByteArray(), android.util.Base64.DEFAULT)); + extras.putBoolean(SenderService.EXTRA_ONLY_SEND_SILENT_REPORTS, onlySendSilentReports); + new JobRequest.Builder(ReportJob.TAG) + .setExecutionWindow(1, TimeUnit.MINUTES.toMillis(1)) + .setExtras(extras) + .setRequirementsEnforced(true) + .setRequiredNetworkType(schedulerConfiguration.requiresNetworkType()) + .setRequiresCharging(schedulerConfiguration.requiresCharging()) + .setRequiresDeviceIdle(schedulerConfiguration.requiresDeviceIdle()) + .setRequiresBatteryNotLow(schedulerConfiguration.requiresBatteryNotLow()) + .setUpdateCurrent(true) + .build() + .schedule(); + } + + @Override + public void setUp(@NonNull Context context, @NonNull CoreConfiguration configuration) { + JobManager.create(context).addJobCreator(new ReportJobCreator()); + } +} diff --git a/acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJob.java b/acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJob.java new file mode 100644 index 0000000000..fbc830ba82 --- /dev/null +++ b/acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJob.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acra.scheduler; + +import android.support.annotation.NonNull; +import android.util.Base64; +import com.evernote.android.job.Job; +import org.acra.config.CoreConfiguration; +import org.acra.sender.SenderService; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; + +/** + * @author F43nd1r + * @since 18.04.18 + */ +public class ReportJob extends Job { + public static final String TAG = "report"; + + @NonNull + @Override + protected Result onRunJob(@NonNull Params params) { + String serializedConfig = params.getExtras().getString(SenderService.EXTRA_ACRA_CONFIG, null); + boolean sendOnlySilentReports = params.getExtras().getBoolean(SenderService.EXTRA_ONLY_SEND_SILENT_REPORTS, false); + if (serializedConfig != null) { + try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(Base64.decode(serializedConfig, Base64.DEFAULT)))) { + CoreConfiguration config = (CoreConfiguration) in.readObject(); + new DefaultSenderScheduler().scheduleReportSending(getContext(), config, sendOnlySilentReports); + } catch (IOException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + return Result.FAILURE; + } +} diff --git a/acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJobCreator.java b/acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJobCreator.java new file mode 100644 index 0000000000..21774322f6 --- /dev/null +++ b/acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJobCreator.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acra.scheduler; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import com.evernote.android.job.Job; +import com.evernote.android.job.JobCreator; + +/** + * @author F43nd1r + * @since 18.04.18 + */ +public class ReportJobCreator implements JobCreator { + @Nullable + @Override + public Job create(@NonNull String tag) { + if (ReportJob.TAG.equals(tag)) { + return new ReportJob(); + } + return null; + } +} diff --git a/acra-core/src/main/java/org/acra/ACRA.java b/acra-core/src/main/java/org/acra/ACRA.java index 2a19efad0a..cdff2831aa 100644 --- a/acra-core/src/main/java/org/acra/ACRA.java +++ b/acra-core/src/main/java/org/acra/ACRA.java @@ -31,6 +31,7 @@ import org.acra.log.AndroidLogDelegate; import org.acra.prefs.SharedPreferencesFactory; import org.acra.reporter.ErrorReporterImpl; +import org.acra.scheduler.SchedulerStarter; import org.acra.util.ApplicationStartupProcessor; import org.acra.util.StreamReader; import org.acra.util.StubCreator; @@ -225,13 +226,14 @@ public static void init(@NonNull Application app, @NonNull CoreConfiguration con final boolean enableAcra = supportedAndroidVersion && SharedPreferencesFactory.shouldEnableACRA(prefs); // Indicate that ACRA is or is not listening for crashes. log.i(LOG_TAG, "ACRA is " + (enableAcra ? "enabled" : "disabled") + " for " + app.getPackageName() + ", initializing..."); - ErrorReporterImpl reporter = new ErrorReporterImpl(app, config, enableAcra, supportedAndroidVersion); + SchedulerStarter schedulerStarter = new SchedulerStarter(app, config); + ErrorReporterImpl reporter = new ErrorReporterImpl(app, config, schedulerStarter, enableAcra, supportedAndroidVersion); errorReporterSingleton = reporter; // Check for approved reports and send them (if enabled). // NB don't check if senderServiceProcess as it will gather these reports itself. if (checkReportsOnApplicationStart) { - new ApplicationStartupProcessor(app, config).checkReports(enableAcra); + new ApplicationStartupProcessor(app, config, schedulerStarter).checkReports(enableAcra); } // register after initAcra is called to avoid a diff --git a/acra-core/src/main/java/org/acra/builder/ReportExecutor.java b/acra-core/src/main/java/org/acra/builder/ReportExecutor.java index 41be918bd0..68b8952741 100644 --- a/acra-core/src/main/java/org/acra/builder/ReportExecutor.java +++ b/acra-core/src/main/java/org/acra/builder/ReportExecutor.java @@ -54,6 +54,7 @@ public class ReportExecutor { private final CoreConfiguration config; private final CrashReportDataFactory crashReportDataFactory; private final List reportingAdministrators; + private final SchedulerStarter schedulerStarter; // A reference to the system's previous default UncaughtExceptionHandler // kept in order to execute the default exception handling after sending the report. @@ -73,13 +74,14 @@ public class ReportExecutor { * @param processFinisher used to end process after reporting */ public ReportExecutor(@NonNull Context context, @NonNull CoreConfiguration config, @NonNull CrashReportDataFactory crashReportDataFactory, - @Nullable Thread.UncaughtExceptionHandler defaultExceptionHandler, @NonNull ProcessFinisher processFinisher) { + @Nullable Thread.UncaughtExceptionHandler defaultExceptionHandler, @NonNull ProcessFinisher processFinisher, @NonNull SchedulerStarter schedulerStarter) { this.context = context; this.config = config; this.crashReportDataFactory = crashReportDataFactory; this.defaultExceptionHandler = defaultExceptionHandler; this.processFinisher = processFinisher; reportingAdministrators = new PluginLoader(config).load(ReportingAdministrator.class); + this.schedulerStarter = schedulerStarter; } /** @@ -226,7 +228,7 @@ private void endApplication(@Nullable Thread uncaughtExceptionThread, Throwable */ private void sendReport(@NonNull File report, boolean onlySendSilentReports) { if (enabled) { - new SchedulerStarter(context, config).scheduleReports(report, onlySendSilentReports); + schedulerStarter.scheduleReports(report, onlySendSilentReports); } else { ACRA.log.w(LOG_TAG, "Would be sending reports, but ACRA is disabled"); } diff --git a/acra-core/src/main/java/org/acra/reporter/ErrorReporterImpl.java b/acra-core/src/main/java/org/acra/reporter/ErrorReporterImpl.java index 539de78f35..2f3e92bed5 100644 --- a/acra-core/src/main/java/org/acra/reporter/ErrorReporterImpl.java +++ b/acra-core/src/main/java/org/acra/reporter/ErrorReporterImpl.java @@ -28,6 +28,7 @@ import org.acra.config.CoreConfiguration; import org.acra.data.CrashReportDataFactory; import org.acra.prefs.SharedPreferencesFactory; +import org.acra.scheduler.SchedulerStarter; import org.acra.util.InstanceCreator; import org.acra.util.ProcessFinisher; @@ -64,7 +65,7 @@ public class ErrorReporterImpl implements Thread.UncaughtExceptionHandler, Share * @param enabled Whether this ErrorReporter should capture Exceptions and forward their reports. * @param supportedAndroidVersion the minimal supported version */ - public ErrorReporterImpl(@NonNull Application context, @NonNull CoreConfiguration config, + public ErrorReporterImpl(@NonNull Application context, @NonNull CoreConfiguration config, @NonNull SchedulerStarter schedulerStarter, boolean enabled, boolean supportedAndroidVersion) { this.context = context; @@ -81,7 +82,7 @@ public ErrorReporterImpl(@NonNull Application context, @NonNull CoreConfiguratio final InstanceCreator instanceCreator = new InstanceCreator(); final ProcessFinisher processFinisher = new ProcessFinisher(context, config, lastActivityManager); - reportExecutor = new ReportExecutor(context, config, crashReportDataFactory, defaultExceptionHandler, processFinisher); + reportExecutor = new ReportExecutor(context, config, crashReportDataFactory, defaultExceptionHandler, processFinisher, schedulerStarter); reportExecutor.setEnabled(enabled); } diff --git a/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java b/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java index 8a634c9de8..4375eedd53 100644 --- a/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java +++ b/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java @@ -51,6 +51,7 @@ public SchedulerStarter(@NonNull Context context, @NonNull CoreConfiguration con senderScheduler = schedulers.get(0); if (schedulers.size() > 1) ACRA.log.w(ACRA.LOG_TAG, "More than one SenderScheduler found. Will use only " + senderScheduler.getClass().getSimpleName()); } + senderScheduler.setUp(context, config); } /** diff --git a/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java b/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java index 1b6d4cd8e5..b295a0f2eb 100644 --- a/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java +++ b/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java @@ -26,5 +26,8 @@ * @since 18.04.18 */ public interface SenderScheduler extends Plugin { + default void setUp(@NonNull Context context, @NonNull CoreConfiguration configuration) { + } + void scheduleReportSending(@NonNull Context context, @NonNull CoreConfiguration config, boolean onlySendSilentReports); } diff --git a/acra-core/src/main/java/org/acra/util/ApplicationStartupProcessor.java b/acra-core/src/main/java/org/acra/util/ApplicationStartupProcessor.java index c798c0933b..187bcee675 100644 --- a/acra-core/src/main/java/org/acra/util/ApplicationStartupProcessor.java +++ b/acra-core/src/main/java/org/acra/util/ApplicationStartupProcessor.java @@ -42,12 +42,14 @@ public final class ApplicationStartupProcessor { private final CoreConfiguration config; private final BulkReportDeleter reportDeleter; private final ReportLocator reportLocator; + private final SchedulerStarter schedulerStarter; - public ApplicationStartupProcessor(@NonNull Context context, @NonNull CoreConfiguration config) { + public ApplicationStartupProcessor(@NonNull Context context, @NonNull CoreConfiguration config, @NonNull SchedulerStarter schedulerStarter) { this.context = context; this.config = config; reportDeleter = new BulkReportDeleter(context); reportLocator = new ReportLocator(context); + this.schedulerStarter = schedulerStarter; } public void checkReports(boolean enableAcra) { @@ -108,7 +110,7 @@ private void sendApprovedReports() { } // Send the approved reports. - new SchedulerStarter(context, config).scheduleReports(null, false); + schedulerStarter.scheduleReports(null, false); } /** diff --git a/gradle.properties b/gradle.properties index add4723bc6..384f6e80c7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -36,4 +36,5 @@ releasePluginVersion=2.6.0 autoServiceVersion=1.0-rc4 junitVersion=4.12 roboelectricVersion=3.8 -asmVersion=6.0 \ No newline at end of file +asmVersion=6.0 +jobVersion=1.2.5 \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index b37c562f0b..0caf8bd637 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':acratest', ':annotationprocessor', ':annotations', ':acra-http', ':acra-core', ':acra-mail', ':acra-dialog', ':acra-toast', ':acra-javacore', ':acra-notification', ':acra-limiter' \ No newline at end of file +include ':acratest', ':annotationprocessor', ':annotations', ':acra-http', ':acra-core', ':acra-mail', ':acra-dialog', ':acra-toast', ':acra-javacore', ':acra-notification', ':acra-limiter', ':acra-advanced-scheduler' \ No newline at end of file From a43b5833fca34aeecb0498494f886b8ce9967727 Mon Sep 17 00:00:00 2001 From: f43nd1r Date: Thu, 19 Apr 2018 00:02:45 +0200 Subject: [PATCH 4/5] javadoc and version update --- .../org/acra/annotation/AcraScheduler.java | 30 ++++++++++++++++++- gradle.properties | 8 ++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/acra-advanced-scheduler/src/main/java/org/acra/annotation/AcraScheduler.java b/acra-advanced-scheduler/src/main/java/org/acra/annotation/AcraScheduler.java index e34bc223d1..626f900598 100644 --- a/acra-advanced-scheduler/src/main/java/org/acra/annotation/AcraScheduler.java +++ b/acra-advanced-scheduler/src/main/java/org/acra/annotation/AcraScheduler.java @@ -16,6 +16,7 @@ package org.acra.annotation; +import android.support.annotation.NonNull; import com.evernote.android.job.JobRequest; import java.lang.annotation.*; @@ -30,8 +31,35 @@ @Inherited @Configuration public @interface AcraScheduler { - JobRequest.NetworkType requiresNetworkType() default JobRequest.NetworkType.ANY; + /** + * Network constraint for report sending + * + * @return networkType required to allow report sending + * @since 5.2.0 + */ + @NonNull JobRequest.NetworkType requiresNetworkType() default JobRequest.NetworkType.ANY; + + /** + * Charging constraint for report sending + * + * @return if reports should only be sent while charging + * @since 5.2.0 + */ boolean requiresCharging() default false; + + /** + * Idle constraint for report sending + * + * @return if reports should only be sent while the device is idle + * @since 5.2.0 + */ boolean requiresDeviceIdle() default false; + + /** + * Battery constraint for report sending + * + * @return if reports should only be sent while battery isn't low + * @since 5.2.0 + */ boolean requiresBatteryNotLow() default false; } diff --git a/gradle.properties b/gradle.properties index 384f6e80c7..cfbc90d9d0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,13 +26,13 @@ org.gradle.caching=true # upload properties group=ch.acra # versions -version=5.1.4-SNAPSHOT -supportVersion=27.0.2 +version=5.2.0-SNAPSHOT +supportVersion=27.1.1 androidVersion=27 androidMinVersion=14 -androidBuildPluginVersion=3.2.0-alpha10 +androidBuildPluginVersion=3.2.0-alpha11 bintrayPluginVersion=1.8.0 -releasePluginVersion=2.6.0 +releasePluginVersion=2.7.0 autoServiceVersion=1.0-rc4 junitVersion=4.12 roboelectricVersion=3.8 From c547290306082ae0d41d1b272e6f6dd23b2c927b Mon Sep 17 00:00:00 2001 From: f43nd1r Date: Fri, 20 Apr 2018 16:24:26 +0200 Subject: [PATCH 5/5] Expose SenderScheduler through ErrorReporter. Closes #650 --- .../scheduler/AdvancedSenderScheduler.java | 57 ++++++++++++------- .../java/org/acra/scheduler/ReportJob.java | 53 ----------------- acra-core/src/main/java/org/acra/ACRA.java | 12 +--- .../src/main/java/org/acra/ErrorReporter.java | 8 +++ .../org/acra/reporter/ErrorReporterImpl.java | 22 +++++-- .../scheduler/DefaultSenderScheduler.java | 12 +++- .../org/acra/scheduler/SchedulerStarter.java | 21 ++++--- .../org/acra/scheduler/SenderScheduler.java | 11 +--- .../scheduler/SenderSchedulerFactory.java | 27 ++++----- 9 files changed, 101 insertions(+), 122 deletions(-) delete mode 100644 acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJob.java rename acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJobCreator.java => acra-core/src/main/java/org/acra/scheduler/SenderSchedulerFactory.java (59%) diff --git a/acra-advanced-scheduler/src/main/java/org/acra/scheduler/AdvancedSenderScheduler.java b/acra-advanced-scheduler/src/main/java/org/acra/scheduler/AdvancedSenderScheduler.java index bf55290222..04506ab0dc 100644 --- a/acra-advanced-scheduler/src/main/java/org/acra/scheduler/AdvancedSenderScheduler.java +++ b/acra-advanced-scheduler/src/main/java/org/acra/scheduler/AdvancedSenderScheduler.java @@ -18,46 +18,45 @@ import android.content.Context; import android.support.annotation.NonNull; +import com.evernote.android.job.Job; import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; import com.evernote.android.job.util.support.PersistableBundleCompat; import com.google.auto.service.AutoService; -import org.acra.ACRA; import org.acra.config.ConfigUtils; import org.acra.config.CoreConfiguration; import org.acra.config.SchedulerConfiguration; +import org.acra.file.ReportLocator; import org.acra.plugins.ConfigBasedAllowsDisablePlugin; import org.acra.sender.SenderService; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; import java.util.concurrent.TimeUnit; /** + * Utilizes evernotes android-job to delay report sending + * * @author F43nd1r * @since 18.04.18 */ -@AutoService(SenderScheduler.class) -public class AdvancedSenderScheduler extends ConfigBasedAllowsDisablePlugin implements SenderScheduler { +public class AdvancedSenderScheduler implements SenderScheduler { + static final String TAG = "org.acra.report.Job"; + private final Context context; + private final CoreConfiguration config; - public AdvancedSenderScheduler() { - super(SchedulerConfiguration.class); + private AdvancedSenderScheduler(@NonNull Context context, @NonNull CoreConfiguration config) { + this.context = context; + this.config = config; } @Override - public void scheduleReportSending(@NonNull Context context, @NonNull CoreConfiguration config, boolean onlySendSilentReports) { + public void scheduleReportSending(boolean onlySendSilentReports) { + if(new ReportLocator(context).getApprovedReports().length == 0) { + return; + } SchedulerConfiguration schedulerConfiguration = ConfigUtils.getPluginConfiguration(config, SchedulerConfiguration.class); PersistableBundleCompat extras = new PersistableBundleCompat(); - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - try(ObjectOutputStream out = new ObjectOutputStream(bytes)){ - out.writeObject(config); - } catch (IOException e) { - ACRA.log.w("Failed to write config to string", e); - } - extras.putString(SenderService.EXTRA_ACRA_CONFIG, android.util.Base64.encodeToString(bytes.toByteArray(), android.util.Base64.DEFAULT)); extras.putBoolean(SenderService.EXTRA_ONLY_SEND_SILENT_REPORTS, onlySendSilentReports); - new JobRequest.Builder(ReportJob.TAG) + new JobRequest.Builder(TAG) .setExecutionWindow(1, TimeUnit.MINUTES.toMillis(1)) .setExtras(extras) .setRequirementsEnforced(true) @@ -70,8 +69,26 @@ public void scheduleReportSending(@NonNull Context context, @NonNull CoreConfigu .schedule(); } - @Override - public void setUp(@NonNull Context context, @NonNull CoreConfiguration configuration) { - JobManager.create(context).addJobCreator(new ReportJobCreator()); + @AutoService(SenderSchedulerFactory.class) + public static class Factory extends ConfigBasedAllowsDisablePlugin implements SenderSchedulerFactory { + + public Factory() { + super(SchedulerConfiguration.class); + } + + @NonNull + @Override + public SenderScheduler create(@NonNull Context context, @NonNull CoreConfiguration config) { + JobManager.create(context).addJobCreator(tag -> TAG.equals(tag) ? new Job() { + @NonNull + @Override + protected Result onRunJob(@NonNull Params params) { + boolean sendOnlySilentReports = params.getExtras().getBoolean(SenderService.EXTRA_ONLY_SEND_SILENT_REPORTS, false); + new DefaultSenderScheduler(getContext(), config).scheduleReportSending(sendOnlySilentReports); + return Result.SUCCESS; + } + } : null); + return new AdvancedSenderScheduler(context, config); + } } } diff --git a/acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJob.java b/acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJob.java deleted file mode 100644 index fbc830ba82..0000000000 --- a/acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJob.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.acra.scheduler; - -import android.support.annotation.NonNull; -import android.util.Base64; -import com.evernote.android.job.Job; -import org.acra.config.CoreConfiguration; -import org.acra.sender.SenderService; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.ObjectInputStream; - -/** - * @author F43nd1r - * @since 18.04.18 - */ -public class ReportJob extends Job { - public static final String TAG = "report"; - - @NonNull - @Override - protected Result onRunJob(@NonNull Params params) { - String serializedConfig = params.getExtras().getString(SenderService.EXTRA_ACRA_CONFIG, null); - boolean sendOnlySilentReports = params.getExtras().getBoolean(SenderService.EXTRA_ONLY_SEND_SILENT_REPORTS, false); - if (serializedConfig != null) { - try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(Base64.decode(serializedConfig, Base64.DEFAULT)))) { - CoreConfiguration config = (CoreConfiguration) in.readObject(); - new DefaultSenderScheduler().scheduleReportSending(getContext(), config, sendOnlySilentReports); - } catch (IOException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - } - return Result.FAILURE; - } -} diff --git a/acra-core/src/main/java/org/acra/ACRA.java b/acra-core/src/main/java/org/acra/ACRA.java index cdff2831aa..260bdca4c1 100644 --- a/acra-core/src/main/java/org/acra/ACRA.java +++ b/acra-core/src/main/java/org/acra/ACRA.java @@ -22,7 +22,6 @@ import android.support.annotation.Keep; import android.support.annotation.NonNull; import android.support.annotation.Nullable; - import org.acra.config.ACRAConfigurationException; import org.acra.config.CoreConfiguration; import org.acra.config.CoreConfigurationBuilder; @@ -31,8 +30,6 @@ import org.acra.log.AndroidLogDelegate; import org.acra.prefs.SharedPreferencesFactory; import org.acra.reporter.ErrorReporterImpl; -import org.acra.scheduler.SchedulerStarter; -import org.acra.util.ApplicationStartupProcessor; import org.acra.util.StreamReader; import org.acra.util.StubCreator; @@ -226,16 +223,9 @@ public static void init(@NonNull Application app, @NonNull CoreConfiguration con final boolean enableAcra = supportedAndroidVersion && SharedPreferencesFactory.shouldEnableACRA(prefs); // Indicate that ACRA is or is not listening for crashes. log.i(LOG_TAG, "ACRA is " + (enableAcra ? "enabled" : "disabled") + " for " + app.getPackageName() + ", initializing..."); - SchedulerStarter schedulerStarter = new SchedulerStarter(app, config); - ErrorReporterImpl reporter = new ErrorReporterImpl(app, config, schedulerStarter, enableAcra, supportedAndroidVersion); + ErrorReporterImpl reporter = new ErrorReporterImpl(app, config, enableAcra, supportedAndroidVersion, checkReportsOnApplicationStart); errorReporterSingleton = reporter; - // Check for approved reports and send them (if enabled). - // NB don't check if senderServiceProcess as it will gather these reports itself. - if (checkReportsOnApplicationStart) { - new ApplicationStartupProcessor(app, config, schedulerStarter).checkReports(enableAcra); - } - // register after initAcra is called to avoid a // NPE in ErrorReporter.disable() because // the context could be null at this moment. diff --git a/acra-core/src/main/java/org/acra/ErrorReporter.java b/acra-core/src/main/java/org/acra/ErrorReporter.java index 947d8fda13..3cb24e4b43 100644 --- a/acra-core/src/main/java/org/acra/ErrorReporter.java +++ b/acra-core/src/main/java/org/acra/ErrorReporter.java @@ -2,6 +2,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import org.acra.scheduler.SenderScheduler; /** * This interface contains methods used to interact with ACRA after it has been initialized @@ -79,4 +80,11 @@ public interface ErrorReporter { * @param e The {@link Throwable} to be reported. If null the report will contain a new Exception("Report requested by developer"). */ void handleException(@Nullable Throwable e); + + /** + * Access point to manual report scheduling + * + * @return current SenderScheduler + */ + SenderScheduler getReportScheduler(); } diff --git a/acra-core/src/main/java/org/acra/reporter/ErrorReporterImpl.java b/acra-core/src/main/java/org/acra/reporter/ErrorReporterImpl.java index 2f3e92bed5..30612780da 100644 --- a/acra-core/src/main/java/org/acra/reporter/ErrorReporterImpl.java +++ b/acra-core/src/main/java/org/acra/reporter/ErrorReporterImpl.java @@ -19,7 +19,6 @@ import android.content.SharedPreferences; import android.support.annotation.NonNull; import android.support.annotation.Nullable; - import org.acra.ACRA; import org.acra.ErrorReporter; import org.acra.builder.LastActivityManager; @@ -29,6 +28,8 @@ import org.acra.data.CrashReportDataFactory; import org.acra.prefs.SharedPreferencesFactory; import org.acra.scheduler.SchedulerStarter; +import org.acra.scheduler.SenderScheduler; +import org.acra.util.ApplicationStartupProcessor; import org.acra.util.InstanceCreator; import org.acra.util.ProcessFinisher; @@ -57,6 +58,7 @@ public class ErrorReporterImpl implements Thread.UncaughtExceptionHandler, Share private final Application context; private final ReportExecutor reportExecutor; private final Map customData = new HashMap<>(); + private final SchedulerStarter schedulerStarter; /** @@ -65,8 +67,8 @@ public class ErrorReporterImpl implements Thread.UncaughtExceptionHandler, Share * @param enabled Whether this ErrorReporter should capture Exceptions and forward their reports. * @param supportedAndroidVersion the minimal supported version */ - public ErrorReporterImpl(@NonNull Application context, @NonNull CoreConfiguration config, @NonNull SchedulerStarter schedulerStarter, - boolean enabled, boolean supportedAndroidVersion) { + public ErrorReporterImpl(@NonNull Application context, @NonNull CoreConfiguration config, + boolean enabled, boolean supportedAndroidVersion, boolean checkReportsOnApplicationStart) { this.context = context; this.supportedAndroidVersion = supportedAndroidVersion; @@ -82,8 +84,15 @@ public ErrorReporterImpl(@NonNull Application context, @NonNull CoreConfiguratio final InstanceCreator instanceCreator = new InstanceCreator(); final ProcessFinisher processFinisher = new ProcessFinisher(context, config, lastActivityManager); + schedulerStarter = new SchedulerStarter(context, config); + reportExecutor = new ReportExecutor(context, config, crashReportDataFactory, defaultExceptionHandler, processFinisher, schedulerStarter); reportExecutor.setEnabled(enabled); + + // Check for approved reports and send them (if enabled). + if (checkReportsOnApplicationStart) { + new ApplicationStartupProcessor(context, config, schedulerStarter).checkReports(enabled); + } } /** @@ -176,7 +185,7 @@ public void setEnabled(boolean enabled) { ACRA.log.i(LOG_TAG, "ACRA is " + (enabled ? "enabled" : "disabled") + " for " + context.getPackageName()); reportExecutor.setEnabled(enabled); } else { - ACRA.log.w(LOG_TAG, "ACRA 4.7.0+ requires Froyo or greater. ACRA is disabled and will NOT catch crashes or send messages."); + ACRA.log.w(LOG_TAG, "ACRA requires ICS or greater. ACRA is disabled and will NOT catch crashes or send messages."); } } @@ -202,6 +211,11 @@ public void handleException(@Nullable Throwable e) { handleException(e, false); } + @Override + public SenderScheduler getReportScheduler() { + return schedulerStarter.getSenderScheduler(); + } + @Override public void onSharedPreferenceChanged(@NonNull SharedPreferences sharedPreferences, @Nullable String key) { if (ACRA.PREF_DISABLE_ACRA.equals(key) || ACRA.PREF_ENABLE_ACRA.equals(key)) { diff --git a/acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java b/acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java index 3ee6feaee2..596ce4ad31 100644 --- a/acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java +++ b/acra-core/src/main/java/org/acra/scheduler/DefaultSenderScheduler.java @@ -27,12 +27,22 @@ import static org.acra.ACRA.LOG_TAG; /** + * Simply schedules sending instantly + * * @author F43nd1r * @since 18.04.18 */ public class DefaultSenderScheduler implements SenderScheduler { + private final Context context; + private final CoreConfiguration config; + + public DefaultSenderScheduler(@NonNull Context context, @NonNull CoreConfiguration config) { + this.context = context; + this.config = config; + } + @Override - public void scheduleReportSending(@NonNull Context context, @NonNull CoreConfiguration config, boolean onlySendSilentReports) { + public void scheduleReportSending(boolean onlySendSilentReports) { if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "About to start SenderService"); final Intent intent = new Intent(); intent.putExtra(SenderService.EXTRA_ONLY_SEND_SILENT_REPORTS, onlySendSilentReports); diff --git a/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java b/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java index 4375eedd53..0cd9f065d6 100644 --- a/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java +++ b/acra-core/src/main/java/org/acra/scheduler/SchedulerStarter.java @@ -35,23 +35,18 @@ */ public class SchedulerStarter { - private final Context context; - private final CoreConfiguration config; private final ReportLocator locator; private final SenderScheduler senderScheduler; public SchedulerStarter(@NonNull Context context, @NonNull CoreConfiguration config) { - this.context = context; - this.config = config; locator = new ReportLocator(context); - List schedulers = new PluginLoader(config).load(SenderScheduler.class); - if (schedulers.isEmpty()) { - senderScheduler = new DefaultSenderScheduler(); + List schedulerFactories = new PluginLoader(config).load(SenderSchedulerFactory.class); + if (schedulerFactories.isEmpty()) { + senderScheduler = new DefaultSenderScheduler(context, config); } else { - senderScheduler = schedulers.get(0); - if (schedulers.size() > 1) ACRA.log.w(ACRA.LOG_TAG, "More than one SenderScheduler found. Will use only " + senderScheduler.getClass().getSimpleName()); + senderScheduler = schedulerFactories.get(0).create(context, config); + if (schedulerFactories.size() > 1) ACRA.log.w(ACRA.LOG_TAG, "More than one SenderScheduler found. Will use only " + senderScheduler.getClass().getSimpleName()); } - senderScheduler.setUp(context, config); } /** @@ -69,6 +64,10 @@ public void scheduleReports(@Nullable File report, boolean onlySendSilentReports } } if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Schedule report sending"); - senderScheduler.scheduleReportSending(context, config, onlySendSilentReports); + senderScheduler.scheduleReportSending(onlySendSilentReports); + } + + public SenderScheduler getSenderScheduler() { + return senderScheduler; } } diff --git a/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java b/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java index b295a0f2eb..151103ac0d 100644 --- a/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java +++ b/acra-core/src/main/java/org/acra/scheduler/SenderScheduler.java @@ -16,18 +16,11 @@ package org.acra.scheduler; -import android.content.Context; -import android.support.annotation.NonNull; -import org.acra.config.CoreConfiguration; -import org.acra.plugins.Plugin; - /** * @author F43nd1r * @since 18.04.18 */ -public interface SenderScheduler extends Plugin { - default void setUp(@NonNull Context context, @NonNull CoreConfiguration configuration) { - } +public interface SenderScheduler { - void scheduleReportSending(@NonNull Context context, @NonNull CoreConfiguration config, boolean onlySendSilentReports); + void scheduleReportSending(boolean onlySendSilentReports); } diff --git a/acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJobCreator.java b/acra-core/src/main/java/org/acra/scheduler/SenderSchedulerFactory.java similarity index 59% rename from acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJobCreator.java rename to acra-core/src/main/java/org/acra/scheduler/SenderSchedulerFactory.java index 21774322f6..6bd7377af0 100644 --- a/acra-advanced-scheduler/src/main/java/org/acra/scheduler/ReportJobCreator.java +++ b/acra-core/src/main/java/org/acra/scheduler/SenderSchedulerFactory.java @@ -16,22 +16,23 @@ package org.acra.scheduler; +import android.content.Context; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.evernote.android.job.Job; -import com.evernote.android.job.JobCreator; +import org.acra.config.CoreConfiguration; +import org.acra.plugins.Plugin; /** * @author F43nd1r - * @since 18.04.18 + * @since 20.04.18 */ -public class ReportJobCreator implements JobCreator { - @Nullable - @Override - public Job create(@NonNull String tag) { - if (ReportJob.TAG.equals(tag)) { - return new ReportJob(); - } - return null; - } +public interface SenderSchedulerFactory extends Plugin { + + + /** + * @param context a context. + * @param config Configuration to use when sending reports. + * @return Fully configured instance of the relevant SenderScheduler. + */ + @NonNull + SenderScheduler create(@NonNull Context context, @NonNull CoreConfiguration config); }