Skip to content

Commit ed3ff88

Browse files
Update crashlytics global data collection (#1434)
* fix tests * add tests * add tests for crashlytics api change * add api txt * make constructor change * use enabled * Refactor and clarify DataCollectionArbiter * Better group related methods Co-authored-by: Matt Willis <[email protected]>
1 parent f1e32c1 commit ed3ff88

File tree

5 files changed

+116
-55
lines changed

5 files changed

+116
-55
lines changed

firebase-crashlytics/api.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ package com.google.firebase.crashlytics {
1010
method public void recordException(@NonNull Throwable);
1111
method public void sendUnsentReports();
1212
method public void setCrashlyticsCollectionEnabled(boolean);
13+
method public void setCrashlyticsCollectionEnabled(@Nullable Boolean);
1314
method public void setCustomKey(@NonNull String, boolean);
1415
method public void setCustomKey(@NonNull String, double);
1516
method public void setCustomKey(@NonNull String, float);

firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/common/CrashlyticsControllerTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,7 @@ public void testUploadDisabledThenEnabled() throws Exception {
828828

829829
// Use a real DataCollectionArbiter to test its switching behavior.
830830
DataCollectionArbiter arbiter = new DataCollectionArbiter(app);
831+
assertFalse(arbiter.isAutomaticDataCollectionEnabled());
831832

832833
final ControllerBuilder builder = builder();
833834
builder.setDataCollectionArbiter(arbiter);
@@ -838,6 +839,21 @@ public void testUploadDisabledThenEnabled() throws Exception {
838839
Task<Void> task = controller.submitAllReports(1.0f, testSettingsDataProvider.getAppSettings());
839840

840841
arbiter.setCrashlyticsDataCollectionEnabled(true);
842+
assertTrue(arbiter.isAutomaticDataCollectionEnabled());
843+
844+
when(mockEditor.putBoolean(PREFS_KEY, false)).thenReturn(mockEditor);
845+
when(mockPrefs.getBoolean(PREFS_KEY, true)).thenReturn(false);
846+
arbiter.setCrashlyticsDataCollectionEnabled(false);
847+
assertFalse(arbiter.isAutomaticDataCollectionEnabled());
848+
849+
when(mockPrefs.contains(PREFS_KEY)).thenReturn(false);
850+
when(mockEditor.remove(PREFS_KEY)).thenReturn(mockEditor);
851+
arbiter.setCrashlyticsDataCollectionEnabled(null);
852+
when(app.isDataCollectionDefaultEnabled()).thenReturn(true);
853+
assertTrue(arbiter.isAutomaticDataCollectionEnabled());
854+
when(app.isDataCollectionDefaultEnabled()).thenReturn(false);
855+
assertFalse(arbiter.isAutomaticDataCollectionEnabled());
856+
841857
await(task);
842858
verify(mockReportManager).areReportsAvailable();
843859
verify(mockReportManager).findReports();

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/FirebaseCrashlytics.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,4 +463,23 @@ public boolean didCrashOnPreviousExecution() {
463463
public void setCrashlyticsCollectionEnabled(boolean enabled) {
464464
core.setCrashlyticsCollectionEnabled(enabled);
465465
}
466+
467+
/**
468+
* Enables/disables/clears automatic data collection config by Crashlytics.
469+
*
470+
* <p>If this is set, it overrides the data collection settings provided by the Android Manifest,
471+
* as well as any Firebase-wide automatic data collection settings.
472+
*
473+
* <p>If automatic data collection is disabled for Crashlytics, crash reports are stored on the
474+
* device. To check for reports, use the {@link #checkForUnsentReports()} method. Use {@link
475+
* #sendUnsentReports()} to upload existing reports even when automatic data collection is
476+
* disabled. Use {@link #deleteUnsentReports()} to delete any reports stored on the device without
477+
* sending them to Crashlytics.
478+
*
479+
* @param enabled whether to enable automatic data collection. The value of null would clear the
480+
* Crashlytics config and the enablement of data collection would depend on other settings.
481+
*/
482+
public void setCrashlyticsCollectionEnabled(@Nullable Boolean enabled) {
483+
core.setCrashlyticsCollectionEnabled(enabled);
484+
}
466485
}

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsCore.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ private Task<Void> doBackgroundInitialization(SettingsDataProvider settingsProvi
247247

248248
// endregion
249249

250-
public void setCrashlyticsCollectionEnabled(boolean enabled) {
250+
public void setCrashlyticsCollectionEnabled(Boolean enabled) {
251251
dataCollectionArbiter.setCrashlyticsDataCollectionEnabled(enabled);
252252
}
253253

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/DataCollectionArbiter.java

Lines changed: 79 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ public class DataCollectionArbiter {
2929
private static final String FIREBASE_CRASHLYTICS_COLLECTION_ENABLED =
3030
"firebase_crashlytics_collection_enabled";
3131

32+
private final SharedPreferences sharedPreferences;
33+
private final FirebaseApp firebaseApp;
34+
3235
// State for waitForDataCollectionEnabled().
33-
private Object taskLock = new Object();
36+
private final Object taskLock = new Object();
3437
TaskCompletionSource<Void> dataCollectionEnabledTask = new TaskCompletionSource<>();
3538
boolean taskResolved = false;
3639

37-
private final SharedPreferences sharedPreferences;
38-
private volatile boolean crashlyticsDataCollectionExplicitlySet;
39-
private volatile boolean crashlyticsDataCollectionEnabled;
40-
private final FirebaseApp firebaseApp;
40+
private Boolean crashlyticsDataCollectionEnabled;
4141

4242
/**
4343
* A Task that will be resolved when explicit data collection permission is granted by calling
@@ -47,43 +47,17 @@ public class DataCollectionArbiter {
4747
new TaskCompletionSource<>();
4848

4949
public DataCollectionArbiter(FirebaseApp app) {
50-
this.firebaseApp = app;
51-
Context applicationContext = app.getApplicationContext();
52-
if (applicationContext == null) {
53-
throw new RuntimeException("null context");
54-
}
50+
final Context applicationContext = app.getApplicationContext();
5551

52+
firebaseApp = app;
5653
sharedPreferences = CommonUtils.getSharedPrefs(applicationContext);
5754

58-
boolean enabled = true;
59-
boolean explicitlySet = false;
60-
61-
if (sharedPreferences.contains(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED)) {
62-
enabled = sharedPreferences.getBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED, true);
63-
explicitlySet = true;
64-
} else {
65-
try {
66-
final PackageManager packageManager = applicationContext.getPackageManager();
67-
if (packageManager != null) {
68-
final ApplicationInfo applicationInfo =
69-
packageManager.getApplicationInfo(
70-
applicationContext.getPackageName(), PackageManager.GET_META_DATA);
71-
if (applicationInfo != null
72-
&& applicationInfo.metaData != null
73-
&& applicationInfo.metaData.containsKey(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED)) {
74-
enabled = applicationInfo.metaData.getBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED);
75-
explicitlySet = true;
76-
}
77-
}
78-
} catch (PackageManager.NameNotFoundException e) {
79-
// This shouldn't happen since it's this app's package, but fall through to default
80-
// if so.
81-
Logger.getLogger().d("Unable to get PackageManager. Falling through", e);
82-
}
55+
Boolean dataCollectionEnabled = getDataCollectionValueFromSharedPreferences(sharedPreferences);
56+
if (dataCollectionEnabled == null) {
57+
dataCollectionEnabled = getDataCollectionValueFromManifest(applicationContext);
8358
}
8459

85-
crashlyticsDataCollectionEnabled = enabled;
86-
crashlyticsDataCollectionExplicitlySet = explicitlySet;
60+
crashlyticsDataCollectionEnabled = dataCollectionEnabled;
8761

8862
synchronized (taskLock) {
8963
if (isAutomaticDataCollectionEnabled()) {
@@ -93,27 +67,21 @@ public DataCollectionArbiter(FirebaseApp app) {
9367
}
9468
}
9569

96-
public boolean isAutomaticDataCollectionEnabled() {
97-
if (crashlyticsDataCollectionExplicitlySet) {
98-
return crashlyticsDataCollectionEnabled;
99-
}
100-
return firebaseApp.isDataCollectionDefaultEnabled();
101-
}
102-
103-
public Task<Void> waitForAutomaticDataCollectionEnabled() {
104-
synchronized (taskLock) {
105-
return dataCollectionEnabledTask.getTask();
106-
}
70+
public synchronized boolean isAutomaticDataCollectionEnabled() {
71+
return crashlyticsDataCollectionEnabled != null
72+
? crashlyticsDataCollectionEnabled
73+
: firebaseApp.isDataCollectionDefaultEnabled();
10774
}
10875

109-
@SuppressLint({"CommitPrefEdits", "ApplySharedPref"})
110-
public void setCrashlyticsDataCollectionEnabled(boolean enabled) {
111-
crashlyticsDataCollectionEnabled = enabled;
112-
crashlyticsDataCollectionExplicitlySet = true;
113-
sharedPreferences.edit().putBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED, enabled).commit();
76+
public synchronized void setCrashlyticsDataCollectionEnabled(Boolean enabled) {
77+
crashlyticsDataCollectionEnabled =
78+
(enabled != null)
79+
? enabled
80+
: getDataCollectionValueFromManifest(firebaseApp.getApplicationContext());
81+
storeDataCollectionValueInSharedPreferences(sharedPreferences, enabled);
11482

11583
synchronized (taskLock) {
116-
if (enabled) {
84+
if (isAutomaticDataCollectionEnabled()) {
11785
if (!taskResolved) {
11886
dataCollectionEnabledTask.trySetResult(null);
11987
taskResolved = true;
@@ -127,6 +95,12 @@ public void setCrashlyticsDataCollectionEnabled(boolean enabled) {
12795
}
12896
}
12997

98+
public Task<Void> waitForAutomaticDataCollectionEnabled() {
99+
synchronized (taskLock) {
100+
return dataCollectionEnabledTask.getTask();
101+
}
102+
}
103+
130104
/**
131105
* Returns a task which will be resolved when either: 1) automatic data collection has been
132106
* enabled, or 2) grantDataCollectionPermission has been called.
@@ -150,4 +124,55 @@ public void grantDataCollectionPermission(boolean dataCollectionToken) {
150124
}
151125
dataCollectionExplicitlyApproved.trySetResult(null);
152126
}
127+
128+
@SuppressLint({"ApplySharedPref"})
129+
private static void storeDataCollectionValueInSharedPreferences(
130+
SharedPreferences sharedPreferences, Boolean enabled) {
131+
final SharedPreferences.Editor prefsEditor = sharedPreferences.edit();
132+
if (enabled != null) {
133+
prefsEditor.putBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED, enabled);
134+
} else {
135+
prefsEditor.remove(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED);
136+
}
137+
prefsEditor.commit();
138+
}
139+
140+
private static Boolean getDataCollectionValueFromSharedPreferences(
141+
SharedPreferences sharedPreferences) {
142+
if (sharedPreferences.contains(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED)) {
143+
return sharedPreferences.getBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED, true);
144+
}
145+
return null;
146+
}
147+
148+
private static Boolean getDataCollectionValueFromManifest(Context applicationContext) {
149+
final Boolean manifestSetting =
150+
readCrashlyticsDataCollectionEnabledFromManifest(applicationContext);
151+
if (manifestSetting == null) {
152+
return null;
153+
}
154+
return Boolean.TRUE.equals(manifestSetting);
155+
}
156+
157+
private static Boolean readCrashlyticsDataCollectionEnabledFromManifest(
158+
Context applicationContext) {
159+
try {
160+
final PackageManager packageManager = applicationContext.getPackageManager();
161+
if (packageManager != null) {
162+
final ApplicationInfo applicationInfo =
163+
packageManager.getApplicationInfo(
164+
applicationContext.getPackageName(), PackageManager.GET_META_DATA);
165+
if (applicationInfo != null
166+
&& applicationInfo.metaData != null
167+
&& applicationInfo.metaData.containsKey(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED)) {
168+
return applicationInfo.metaData.getBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED);
169+
}
170+
}
171+
} catch (PackageManager.NameNotFoundException e) {
172+
// This shouldn't happen since it's this app's package, but fall through to default
173+
// if so.
174+
Logger.getLogger().d("Unable to get PackageManager. Falling through", e);
175+
}
176+
return null;
177+
}
153178
}

0 commit comments

Comments
 (0)