[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update crashlytics global data collection #1434

Merged
merged 10 commits into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions firebase-crashlytics/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package com.google.firebase.crashlytics {
method public void recordException(@NonNull Throwable);
method public void sendUnsentReports();
method public void setCrashlyticsCollectionEnabled(boolean);
method public void setCrashlyticsCollectionEnabled(@Nullable Boolean);
method public void setCustomKey(@NonNull String, boolean);
method public void setCustomKey(@NonNull String, double);
method public void setCustomKey(@NonNull String, float);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,7 @@ public void testUploadDisabledThenEnabled() throws Exception {

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

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

arbiter.setCrashlyticsDataCollectionEnabled(true);
assertTrue(arbiter.isAutomaticDataCollectionEnabled());

when(mockEditor.putBoolean(PREFS_KEY, false)).thenReturn(mockEditor);
when(mockPrefs.getBoolean(PREFS_KEY, true)).thenReturn(false);
arbiter.setCrashlyticsDataCollectionEnabled(false);
assertFalse(arbiter.isAutomaticDataCollectionEnabled());

when(mockPrefs.contains(PREFS_KEY)).thenReturn(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you split this up into separate tests that express the conditions they check?

when(mockEditor.remove(PREFS_KEY)).thenReturn(mockEditor);
arbiter.setCrashlyticsDataCollectionEnabled(null);
when(app.isDataCollectionDefaultEnabled()).thenReturn(true);
assertTrue(arbiter.isAutomaticDataCollectionEnabled());
when(app.isDataCollectionDefaultEnabled()).thenReturn(false);
assertFalse(arbiter.isAutomaticDataCollectionEnabled());

await(task);
verify(mockReportManager).areReportsAvailable();
verify(mockReportManager).findReports();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,4 +463,23 @@ public boolean didCrashOnPreviousExecution() {
public void setCrashlyticsCollectionEnabled(boolean enabled) {
core.setCrashlyticsCollectionEnabled(enabled);
}

/**
* Enables/disables/clears automatic data collection config by Crashlytics.
Copy link
@cbonnie cbonnie Aug 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest replacing line 468 with:

"* Allows you to enable or disable the automatic data collection configuration in Crashlytics."

*
* <p>If this is set, it overrides the data collection settings provided by the Android Manifest,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest replacing lines 470 and 471 with:

"* If not set, defaults to True. When set to False, disables automatic collection by overriding the data collections settings in the AndroidManifest.xml, as well as any Firebase-wide automatic data collection settings. When set to null, clears the Crashlytics data collection configuration (in this case, enabling data collection becomes dependent on other settings)."

* as well as any Firebase-wide automatic data collection settings.
*
* <p>If automatic data collection is disabled for Crashlytics, crash reports are stored on the
* device. To check for reports, use the {@link #checkForUnsentReports()} method. Use {@link
* #sendUnsentReports()} to upload existing reports even when automatic data collection is
* disabled. Use {@link #deleteUnsentReports()} to delete any reports stored on the device without
* sending them to Crashlytics.
*
* @param enabled whether to enable automatic data collection. The value of null would clear the
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest replacing line 479 with:

"@param Determines whether to enable or disable automatic data collection. If set to null, clears the Crashlytics data collection configuration."

* Crashlytics config and the enablement of data collection would depend on other settings.
*/
public void setCrashlyticsCollectionEnabled(@Nullable Boolean enabled) {
core.setCrashlyticsCollectionEnabled(enabled);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ private Task<Void> doBackgroundInitialization(SettingsDataProvider settingsProvi

// endregion

public void setCrashlyticsCollectionEnabled(boolean enabled) {
public void setCrashlyticsCollectionEnabled(Boolean enabled) {
dataCollectionArbiter.setCrashlyticsDataCollectionEnabled(enabled);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ public class DataCollectionArbiter {
private static final String FIREBASE_CRASHLYTICS_COLLECTION_ENABLED =
"firebase_crashlytics_collection_enabled";

private final SharedPreferences sharedPreferences;
private final FirebaseApp firebaseApp;

// State for waitForDataCollectionEnabled().
private Object taskLock = new Object();
private final Object taskLock = new Object();
TaskCompletionSource<Void> dataCollectionEnabledTask = new TaskCompletionSource<>();
boolean taskResolved = false;

private final SharedPreferences sharedPreferences;
private volatile boolean crashlyticsDataCollectionExplicitlySet;
private volatile boolean crashlyticsDataCollectionEnabled;
private final FirebaseApp firebaseApp;
private Boolean crashlyticsDataCollectionEnabled;

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

public DataCollectionArbiter(FirebaseApp app) {
this.firebaseApp = app;
Context applicationContext = app.getApplicationContext();
if (applicationContext == null) {
throw new RuntimeException("null context");
}
final Context applicationContext = app.getApplicationContext();

firebaseApp = app;
sharedPreferences = CommonUtils.getSharedPrefs(applicationContext);

boolean enabled = true;
boolean explicitlySet = false;

if (sharedPreferences.contains(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED)) {
enabled = sharedPreferences.getBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED, true);
explicitlySet = true;
} else {
try {
final PackageManager packageManager = applicationContext.getPackageManager();
if (packageManager != null) {
final ApplicationInfo applicationInfo =
packageManager.getApplicationInfo(
applicationContext.getPackageName(), PackageManager.GET_META_DATA);
if (applicationInfo != null
&& applicationInfo.metaData != null
&& applicationInfo.metaData.containsKey(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED)) {
enabled = applicationInfo.metaData.getBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED);
explicitlySet = true;
}
}
} catch (PackageManager.NameNotFoundException e) {
// This shouldn't happen since it's this app's package, but fall through to default
// if so.
Logger.getLogger().d("Unable to get PackageManager. Falling through", e);
}
Boolean dataCollectionEnabled = getDataCollectionValueFromSharedPreferences(sharedPreferences);
if (dataCollectionEnabled == null) {
dataCollectionEnabled = getDataCollectionValueFromManifest(applicationContext);
}

crashlyticsDataCollectionEnabled = enabled;
crashlyticsDataCollectionExplicitlySet = explicitlySet;
crashlyticsDataCollectionEnabled = dataCollectionEnabled;

synchronized (taskLock) {
if (isAutomaticDataCollectionEnabled()) {
Expand All @@ -93,27 +67,21 @@ public DataCollectionArbiter(FirebaseApp app) {
}
}

public boolean isAutomaticDataCollectionEnabled() {
if (crashlyticsDataCollectionExplicitlySet) {
return crashlyticsDataCollectionEnabled;
}
return firebaseApp.isDataCollectionDefaultEnabled();
}

public Task<Void> waitForAutomaticDataCollectionEnabled() {
synchronized (taskLock) {
return dataCollectionEnabledTask.getTask();
}
public synchronized boolean isAutomaticDataCollectionEnabled() {
return crashlyticsDataCollectionEnabled != null
? crashlyticsDataCollectionEnabled
: firebaseApp.isDataCollectionDefaultEnabled();
}

@SuppressLint({"CommitPrefEdits", "ApplySharedPref"})
public void setCrashlyticsDataCollectionEnabled(boolean enabled) {
crashlyticsDataCollectionEnabled = enabled;
crashlyticsDataCollectionExplicitlySet = true;
sharedPreferences.edit().putBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED, enabled).commit();
public synchronized void setCrashlyticsDataCollectionEnabled(Boolean enabled) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can help our future selves by adding some comments here :)

mrwillis21 marked this conversation as resolved.
Show resolved Hide resolved
crashlyticsDataCollectionEnabled =
(enabled != null)
? enabled
: getDataCollectionValueFromManifest(firebaseApp.getApplicationContext());
storeDataCollectionValueInSharedPreferences(sharedPreferences, enabled);

synchronized (taskLock) {
if (enabled) {
if (isAutomaticDataCollectionEnabled()) {
if (!taskResolved) {
dataCollectionEnabledTask.trySetResult(null);
taskResolved = true;
Expand All @@ -127,6 +95,12 @@ public void setCrashlyticsDataCollectionEnabled(boolean enabled) {
}
}

public Task<Void> waitForAutomaticDataCollectionEnabled() {
synchronized (taskLock) {
return dataCollectionEnabledTask.getTask();
}
}

/**
* Returns a task which will be resolved when either: 1) automatic data collection has been
* enabled, or 2) grantDataCollectionPermission has been called.
Expand All @@ -150,4 +124,55 @@ public void grantDataCollectionPermission(boolean dataCollectionToken) {
}
dataCollectionExplicitlyApproved.trySetResult(null);
}

@SuppressLint({"ApplySharedPref"})
private static void storeDataCollectionValueInSharedPreferences(
SharedPreferences sharedPreferences, Boolean enabled) {
final SharedPreferences.Editor prefsEditor = sharedPreferences.edit();
if (enabled != null) {
prefsEditor.putBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED, enabled);
} else {
prefsEditor.remove(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED);
}
prefsEditor.commit();
}

private static Boolean getDataCollectionValueFromSharedPreferences(
SharedPreferences sharedPreferences) {
if (sharedPreferences.contains(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED)) {
return sharedPreferences.getBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED, true);
}
return null;
}

private static Boolean getDataCollectionValueFromManifest(Context applicationContext) {
final Boolean manifestSetting =
readCrashlyticsDataCollectionEnabledFromManifest(applicationContext);
if (manifestSetting == null) {
return null;
}
return Boolean.TRUE.equals(manifestSetting);
}

private static Boolean readCrashlyticsDataCollectionEnabledFromManifest(
Context applicationContext) {
try {
final PackageManager packageManager = applicationContext.getPackageManager();
if (packageManager != null) {
final ApplicationInfo applicationInfo =
packageManager.getApplicationInfo(
applicationContext.getPackageName(), PackageManager.GET_META_DATA);
if (applicationInfo != null
&& applicationInfo.metaData != null
&& applicationInfo.metaData.containsKey(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED)) {
return applicationInfo.metaData.getBoolean(FIREBASE_CRASHLYTICS_COLLECTION_ENABLED);
}
}
} catch (PackageManager.NameNotFoundException e) {
// This shouldn't happen since it's this app's package, but fall through to default
// if so.
Logger.getLogger().d("Unable to get PackageManager. Falling through", e);
}
return null;
}
}