Skip to content

Commit d01d7fe

Browse files
authored
Merge pull request #1489 from microsoft/develop
Version 4.1.0
2 parents 38c8b23 + 4e5bf62 commit d01d7fe

File tree

18 files changed

+498
-163
lines changed

18 files changed

+498
-163
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
# App Center SDK for Android Change Log
22

3+
## Version 4.1.0
4+
5+
### App Center Crashes
6+
7+
* **[Fix]** Fix removing throwable files after rewriting error logs due to small database size.
8+
9+
### App Center Distribute
10+
11+
* **[Feature]** Add `onNoReleaseAvailable` callback to DistributeListener.
12+
* **[Fix]** Fix a crash when the app is trying to open the system settings screen from the background.
13+
* **[Fix]** Fix browser opening when using a private distribution group on Android 11.
14+
15+
___
16+
317
## Version 4.0.0
418

519
### App Center

apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/listeners/SasquatchDistributeListener.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import android.app.Activity;
99
import android.content.DialogInterface;
1010
import android.support.v7.app.AlertDialog;
11+
import android.widget.Toast;
1112

1213
import com.microsoft.appcenter.distribute.Distribute;
1314
import com.microsoft.appcenter.distribute.DistributeListener;
@@ -46,4 +47,15 @@ public void onClick(DialogInterface dialog, int which) {
4647
}
4748
return custom;
4849
}
50+
51+
/*
52+
* TODO: uncomment the annotation after release.
53+
* The annotation conflicts with `gradlew assemble` command, because gradle runs through
54+
* all build variants including jcenter ones.
55+
*
56+
* @Override
57+
*/
58+
public void onNoReleaseAvailable(Activity activity) {
59+
Toast.makeText(activity, activity.getString(R.string.no_updates_available), Toast.LENGTH_LONG).show();
60+
}
4961
}

apps/sasquatch/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
<item>Date/Time</item>
5454
<item>Clear</item>
5555
</string-array>
56+
<string name="no_updates_available" tools:ignore="MissingTranslation">No updates available</string>
5657
<string name="version_x_available" tools:ignore="MissingTranslation">Version %1$s available!</string>
5758
<string name="transmission_enabled" tools:ignore="MissingTranslation">Transmission target is enabled.</string>
5859
<string name="transmission_disabled" tools:ignore="MissingTranslation">Transmission target is disabled.</string>

sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ public void run() {
347347
verify(uncaughtExceptionHandler).uncaughtException(thread, exception);
348348
File[] files = ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter);
349349
assertNotNull(files);
350-
assertEquals(2, files.length);
350+
assertEquals(1, files.length);
351351
verifyZeroInteractions(crashesListener);
352352

353353
/* Second process: enqueue log but network is down... */
@@ -380,7 +380,7 @@ public void run() {
380380
verify(mChannel, never()).enqueue(isA(ManagedErrorLog.class), anyString(), anyInt());
381381
files = ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter);
382382
assertNotNull(files);
383-
assertEquals(2, files.length);
383+
assertEquals(1, files.length);
384384
verify(crashesListener).shouldProcess(any(ErrorReport.class));
385385
verify(crashesListener).shouldAwaitUserConfirmation();
386386
verifyNoMoreInteractions(crashesListener);
@@ -394,7 +394,7 @@ public void run() {
394394
assertEquals(mUserId, log.getValue().getUserId());
395395
files = ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter);
396396
assertNotNull(files);
397-
assertEquals(1, files.length);
397+
assertEquals(0, files.length);
398398

399399
verify(crashesListener).getErrorAttachments(any(ErrorReport.class));
400400
verifyNoMoreInteractions(crashesListener);
@@ -474,7 +474,7 @@ public void run() {
474474
verify(mChannel, never()).enqueue(isA(ManagedErrorLog.class), anyString(), anyInt());
475475
File[] files = ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter);
476476
assertNotNull(files);
477-
assertEquals(2, files.length);
477+
assertEquals(1, files.length);
478478
verify(crashesListener).shouldProcess(any(ErrorReport.class));
479479
verify(crashesListener).shouldAwaitUserConfirmation();
480480
verifyNoMoreInteractions(crashesListener);
@@ -490,7 +490,7 @@ public void run() {
490490
assertNull(managedErrorLog.getValue().getException().getMinidumpFilePath());
491491
files = ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter);
492492
assertNotNull(files);
493-
assertEquals(1, files.length);
493+
assertEquals(0, files.length);
494494
verify(crashesListener).getErrorAttachments(any(ErrorReport.class));
495495
verifyNoMoreInteractions(crashesListener);
496496

@@ -533,7 +533,7 @@ public void run() {
533533
verify(uncaughtExceptionHandler).uncaughtException(thread, exception);
534534
File[] files = ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter);
535535
assertNotNull(files);
536-
assertEquals(2, files.length);
536+
assertEquals(1, files.length);
537537

538538
/* Disable, test waiting for disable to finish. */
539539
Crashes.setEnabled(false).get();
@@ -568,7 +568,7 @@ public void run() {
568568
/* Check there are only 2 files: the throwable and the json one. */
569569
File[] files = ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter);
570570
assertNotNull(files);
571-
assertEquals(2, files.length);
571+
assertEquals(1, files.length);
572572
}
573573

574574
@Test
@@ -617,7 +617,7 @@ public void run() {
617617
verify(mChannel, never()).enqueue(isA(ManagedErrorLog.class), anyString(), anyInt());
618618
File[] files = ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter);
619619
assertNotNull(files);
620-
assertEquals(4, files.length);
620+
assertEquals(2, files.length);
621621
verify(crashesListener, times(2)).shouldProcess(any(ErrorReport.class));
622622
verify(crashesListener).shouldAwaitUserConfirmation();
623623
verifyNoMoreInteractions(crashesListener);
@@ -633,7 +633,7 @@ public void run() {
633633
assertNull(managedErrorLog.getValue().getException().getMinidumpFilePath());
634634
files = ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter);
635635
assertNotNull(files);
636-
assertEquals(2, files.length);
636+
assertEquals(0, files.length);
637637
verify(crashesListener, times(2)).getErrorAttachments(any(ErrorReport.class));
638638
verifyNoMoreInteractions(crashesListener);
639639

sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperAndroidTest.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -118,33 +118,16 @@ public void getStoredFile() throws Exception {
118118
file = ErrorLogHelper.getStoredErrorLogFile(new UUID(0, 3));
119119
assertNull(file);
120120

121-
/* Get a throwable file by UUID. */
122-
file = ErrorLogHelper.getStoredThrowableFile(new UUID(0, 3));
123-
assertNotNull(file);
124-
assertEquals(testFiles[3], file);
125-
file = ErrorLogHelper.getStoredThrowableFile(new UUID(0, 0));
126-
assertNull(file);
127-
128121
/* Remove an error log file. */
129122
ErrorLogHelper.removeStoredErrorLogFile(new UUID(0, 2));
130123
file = ErrorLogHelper.getStoredErrorLogFile(new UUID(0, 2));
131124
assertNull(file);
132125

133-
/*Trying to delete an error log file which doesn't exist. */
134-
ErrorLogHelper.removeStoredErrorLogFile(new UUID(0, 3));
135-
file = ErrorLogHelper.getStoredThrowableFile(new UUID(0, 3));
136-
assertNotNull(file);
137-
138126
/* Verify the number of remaining error log files. */
139127
files = ErrorLogHelper.getStoredErrorLogFiles();
140128
assertNotNull(files);
141129
assertEquals(2, files.length);
142130

143-
/* Remove a throwable file. */
144-
ErrorLogHelper.removeStoredThrowableFile(new UUID(0, 3));
145-
file = ErrorLogHelper.getStoredThrowableFile(new UUID(0, 3));
146-
assertNull(file);
147-
148131
/*Trying to delete a throwable file which doesn't exist. */
149132
ErrorLogHelper.removeStoredThrowableFile(new UUID(0, 1));
150133
file = ErrorLogHelper.getStoredErrorLogFile(new UUID(0, 1));

sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java

Lines changed: 40 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.microsoft.appcenter.crashes.ingestion.models.Exception;
2323
import com.microsoft.appcenter.crashes.ingestion.models.HandledErrorLog;
2424
import com.microsoft.appcenter.crashes.ingestion.models.ManagedErrorLog;
25+
import com.microsoft.appcenter.crashes.ingestion.models.StackFrame;
2526
import com.microsoft.appcenter.crashes.ingestion.models.json.ErrorAttachmentLogFactory;
2627
import com.microsoft.appcenter.crashes.ingestion.models.json.HandledErrorLogFactory;
2728
import com.microsoft.appcenter.crashes.ingestion.models.json.ManagedErrorLogFactory;
@@ -106,6 +107,12 @@ public class Crashes extends AbstractAppCenterService {
106107
@VisibleForTesting
107108
static final String ERROR_GROUP = "groupErrors";
108109

110+
/**
111+
* Minidump file.
112+
*/
113+
@VisibleForTesting
114+
static final String MINIDUMP_FILE = "minidump";
115+
109116
/**
110117
* Name of the service.
111118
*/
@@ -472,6 +479,12 @@ public synchronized void onStarted(@NonNull Context context, @NonNull Channel ch
472479
super.onStarted(context, channel, appSecret, transmissionTargetToken, startedFromApp);
473480
if (isInstanceEnabled()) {
474481
processPendingErrors();
482+
483+
if (mErrorReportCache.isEmpty()) {
484+
485+
/* Remove lost throwable files. */
486+
ErrorLogHelper.removeLostThrowableFiles();
487+
}
475488
}
476489
}
477490

@@ -516,11 +529,6 @@ public void run() {
516529
UUID id = errorLog.getId();
517530
if (report != null) {
518531

519-
/* Clean up before calling callbacks if requested. */
520-
if (callbackProcessor.shouldDeleteThrowable()) {
521-
removeStoredThrowable(id);
522-
}
523-
524532
/* Call back. */
525533
HandlerUtils.runOnUiThread(new Runnable() {
526534

@@ -543,11 +551,6 @@ public void run() {
543551
public void onBeforeSending(Log log) {
544552
processCallback(log, new CallbackProcessor() {
545553

546-
@Override
547-
public boolean shouldDeleteThrowable() {
548-
return false;
549-
}
550-
551554
@Override
552555
public void onCallBack(ErrorReport report) {
553556
mCrashesListener.onBeforeSending(report);
@@ -559,11 +562,6 @@ public void onCallBack(ErrorReport report) {
559562
public void onSuccess(Log log) {
560563
processCallback(log, new CallbackProcessor() {
561564

562-
@Override
563-
public boolean shouldDeleteThrowable() {
564-
return true;
565-
}
566-
567565
@Override
568566
public void onCallBack(ErrorReport report) {
569567
mCrashesListener.onSendingSucceeded(report);
@@ -575,11 +573,6 @@ public void onCallBack(ErrorReport report) {
575573
public void onFailure(Log log, final java.lang.Exception e) {
576574
processCallback(log, new CallbackProcessor() {
577575

578-
@Override
579-
public boolean shouldDeleteThrowable() {
580-
return true;
581-
}
582-
583576
@Override
584577
public void onCallBack(ErrorReport report) {
585578
mCrashesListener.onSendingFailed(report, e);
@@ -917,7 +910,6 @@ private void removeAllStoredErrorLogFiles(UUID id) {
917910
private void removeStoredThrowable(UUID id) {
918911
mErrorReportCache.remove(id);
919912
WrapperSdkExceptionManager.deleteWrapperExceptionData(id);
920-
ErrorLogHelper.removeStoredThrowableFile(id);
921913
}
922914

923915
@VisibleForTesting
@@ -931,26 +923,45 @@ void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
931923
}
932924

933925
@VisibleForTesting
934-
@Nullable
926+
String buildStackTrace(Exception exception) {
927+
String stacktrace = String.format("%s: %s", exception.getType(), exception.getMessage());
928+
if (exception.getFrames() == null) {
929+
return stacktrace;
930+
}
931+
for (StackFrame frame : exception.getFrames()) {
932+
stacktrace += String.format("\n %s.%s(%s:%s)", frame.getClassName(), frame.getMethodName(), frame.getFileName(), frame.getLineNumber());
933+
}
934+
return stacktrace;
935+
}
936+
937+
@VisibleForTesting
935938
ErrorReport buildErrorReport(ManagedErrorLog log) {
936939
UUID id = log.getId();
937940
if (mErrorReportCache.containsKey(id)) {
938941
ErrorReport report = mErrorReportCache.get(id).report;
939942
report.setDevice(log.getDevice());
940943
return report;
941944
} else {
945+
String stackTrace = null;
946+
947+
/* If exception in the log doesn't have stack trace try get it from the .throwable file. */
942948
File file = ErrorLogHelper.getStoredThrowableFile(id);
943949
if (file != null) {
944-
String stackTrace = null;
945950
if (file.length() > 0) {
946951
stackTrace = FileManager.read(file);
947952
}
948-
ErrorReport report = ErrorLogHelper.getErrorReportFromErrorLog(log, stackTrace);
949-
mErrorReportCache.put(id, new ErrorLogReport(log, report));
950-
return report;
951953
}
954+
if (stackTrace == null) {
955+
if (MINIDUMP_FILE.equals(log.getException().getType())) {
956+
stackTrace = getStackTraceString(new NativeException());
957+
} else {
958+
stackTrace = buildStackTrace(log.getException());
959+
}
960+
}
961+
ErrorReport report = ErrorLogHelper.getErrorReportFromErrorLog(log, stackTrace);
962+
mErrorReportCache.put(id, new ErrorLogReport(log, report));
963+
return report;
952964
}
953-
return null;
954965
}
955966

956967
@VisibleForTesting
@@ -1148,34 +1159,11 @@ private UUID saveErrorLogFiles(Throwable throwable, ManagedErrorLog errorLog) th
11481159
String filename = errorLogId.toString();
11491160
AppCenterLog.debug(Crashes.LOG_TAG, "Saving uncaught exception.");
11501161
File errorLogFile = new File(errorStorageDirectory, filename + ErrorLogHelper.ERROR_LOG_FILE_EXTENSION);
1162+
1163+
/* Save stacktrace log to file. */
11511164
String errorLogString = mLogSerializer.serializeLog(errorLog);
11521165
FileManager.write(errorLogFile, errorLogString);
11531166
AppCenterLog.debug(Crashes.LOG_TAG, "Saved JSON content for ingestion into " + errorLogFile);
1154-
File throwableFile = new File(errorStorageDirectory, filename + ErrorLogHelper.THROWABLE_FILE_EXTENSION);
1155-
if (throwable != null) {
1156-
try {
1157-
String stackTrace = getStackTraceString(throwable);
1158-
FileManager.write(throwableFile, stackTrace);
1159-
AppCenterLog.debug(LOG_TAG, "Saved stack trace as is for client side inspection in " + throwableFile + " stack trace:" + stackTrace);
1160-
} catch (StackOverflowError e) {
1161-
AppCenterLog.error(Crashes.LOG_TAG, "Failed to store stack trace.", e);
1162-
throwable = null;
1163-
1164-
//noinspection ResultOfMethodCallIgnored
1165-
throwableFile.delete();
1166-
}
1167-
}
1168-
if (throwable == null) {
1169-
1170-
/*
1171-
* If there is no Java Throwable to save as is (typical in wrapper SDKs),
1172-
* use file placeholder as we also use this file to manage state.
1173-
*/
1174-
if (!throwableFile.createNewFile()) {
1175-
throw new IOException(throwableFile.getName());
1176-
}
1177-
AppCenterLog.debug(Crashes.LOG_TAG, "Saved empty Throwable file in " + throwableFile);
1178-
}
11791167
return errorLogId;
11801168
}
11811169

@@ -1287,11 +1275,6 @@ private interface ExceptionModelBuilder {
12871275
*/
12881276
private interface CallbackProcessor {
12891277

1290-
/**
1291-
* @return true to delete the stored serialized throwable file.
1292-
*/
1293-
boolean shouldDeleteThrowable();
1294-
12951278
/**
12961279
* Execute call back.
12971280
*

sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.microsoft.appcenter.crashes.ingestion.models.Thread;
2323
import com.microsoft.appcenter.crashes.model.ErrorReport;
2424
import com.microsoft.appcenter.ingestion.models.Device;
25+
import com.microsoft.appcenter.ingestion.models.Log;
2526
import com.microsoft.appcenter.utils.AppCenterLog;
2627
import com.microsoft.appcenter.utils.DeviceInfoHelper;
2728
import com.microsoft.appcenter.utils.context.UserIdContext;
@@ -438,6 +439,24 @@ public static void removeStoredErrorLogFile(@NonNull UUID id) {
438439
}
439440
}
440441

442+
/**
443+
* Remove throwable files.
444+
*/
445+
public static void removeLostThrowableFiles() {
446+
File[] throwableFiles = getErrorStorageDirectory().listFiles(new FilenameFilter() {
447+
448+
@Override
449+
public boolean accept(File dir, String filename) {
450+
return filename.endsWith(THROWABLE_FILE_EXTENSION);
451+
}
452+
});
453+
if (throwableFiles != null && throwableFiles.length > 0) {
454+
for (File file : throwableFiles) {
455+
removeStoredThrowableFile(UUID.fromString(file.getName().replaceFirst("\\.[^.]+$", "")));
456+
}
457+
}
458+
}
459+
441460
@NonNull
442461
public static ErrorReport getErrorReportFromErrorLog(@NonNull ManagedErrorLog log, String stackTrace) {
443462
ErrorReport report = new ErrorReport();

0 commit comments

Comments
 (0)