Skip to content

Commit 7fae8d7

Browse files
Merge branch 'master' into flaky-tests-fix-2
2 parents 86f560a + c42e6bc commit 7fae8d7

4 files changed

Lines changed: 34 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

55
## [Unreleased]
6+
### Fixed
7+
- Fixed `ConcurrentModificationException` crash during device token registration caused by concurrent access to `deviceAttributes`.
8+
- Fixed possible `NoSuchMethodException` crash on Android 5-10 caused by using `Map.of()` which is unavailable on those versions
69

710
## [3.7.0]
811
- Replaced the deprecated `AsyncTask`-based push notification handling with `WorkManager` for improved reliability and compatibility with modern Android versions. No action is required.

iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
import org.json.JSONException;
1818
import org.json.JSONObject;
1919

20-
import java.util.HashMap;
2120
import java.util.List;
21+
import java.util.Map;
2222
import java.util.Objects;
2323
import java.util.UUID;
24+
import java.util.concurrent.ConcurrentHashMap;
2425

2526
/**
2627
* Created by David Truong dt@iterable.com
@@ -55,7 +56,7 @@ public class IterableApi {
5556
private @Nullable IterableEmbeddedManager embeddedManager;
5657
private String inboxSessionId;
5758
private IterableAuthManager authManager;
58-
private HashMap<String, String> deviceAttributes = new HashMap<>();
59+
private ConcurrentHashMap<String, String> deviceAttributes = new ConcurrentHashMap<>();
5960
private IterableKeychain keychain;
6061

6162

@@ -159,7 +160,7 @@ void setAttributionInfo(IterableAttributionInfo attributionInfo) {
159160
);
160161
}
161162

162-
HashMap getDeviceAttributes() {
163+
Map<String, String> getDeviceAttributes() {
163164
return deviceAttributes;
164165
}
165166

@@ -695,7 +696,7 @@ void setAuthToken(String authToken, boolean bypassAuth) {
695696
}
696697
}
697698

698-
protected void registerDeviceToken(final @Nullable String email, final @Nullable String userId, final @Nullable String authToken, final @NonNull String applicationName, final @NonNull String deviceToken, final HashMap<String, String> deviceAttributes) {
699+
protected void registerDeviceToken(final @Nullable String email, final @Nullable String userId, final @Nullable String authToken, final @NonNull String applicationName, final @NonNull String deviceToken, final Map<String, String> deviceAttributes) {
699700
if (deviceToken != null) {
700701
if (!checkSDKInitialization() && _userIdUnknown == null) {
701702
if (sharedInstance.config.enableUnknownUserActivation) {
@@ -740,7 +741,7 @@ protected void disableToken(@Nullable String email, @Nullable String userId, @Nu
740741
* @param deviceToken
741742
* @param dataFields
742743
*/
743-
protected void registerDeviceToken(@Nullable String email, @Nullable String userId, @Nullable String authToken, @NonNull String applicationName, @NonNull String deviceToken, @Nullable JSONObject dataFields, HashMap<String, String> deviceAttributes) {
744+
protected void registerDeviceToken(@Nullable String email, @Nullable String userId, @Nullable String authToken, @NonNull String applicationName, @NonNull String deviceToken, @Nullable JSONObject dataFields, Map<String, String> deviceAttributes) {
744745
if (!checkSDKInitialization()) {
745746
return;
746747
}
@@ -1245,10 +1246,18 @@ public Bundle getPayloadData() {
12451246
}
12461247

12471248
public void setDeviceAttribute(String key, String value) {
1249+
if (key == null || value == null) {
1250+
IterableLogger.e(TAG, "setDeviceAttribute: key and value must not be null");
1251+
return;
1252+
}
12481253
deviceAttributes.put(key, value);
12491254
}
12501255

12511256
public void removeDeviceAttribute(String key) {
1257+
if (key == null) {
1258+
IterableLogger.e(TAG, "removeDeviceAttribute: key must not be null");
1259+
return;
1260+
}
12521261
deviceAttributes.remove(key);
12531262
}
12541263
//endregion

iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
import org.json.JSONObject;
1515

1616
import java.util.Arrays;
17-
import java.util.HashMap;
1817
import java.util.List;
18+
import java.util.Map;
1919

2020
class IterableApiClient {
2121
private static final String TAG = "IterableApiClient";
@@ -623,7 +623,7 @@ protected void disableToken(@Nullable String email, @Nullable String userId, @Nu
623623
}
624624
}
625625

626-
protected void registerDeviceToken(@Nullable String email, @Nullable String userId, @Nullable String authToken, @NonNull String applicationName, @NonNull String deviceToken, @Nullable JSONObject dataFields, HashMap<String, String> deviceAttributes, @Nullable final IterableHelper.SuccessHandler successHandler, @Nullable final IterableHelper.FailureHandler failureHandler) {
626+
protected void registerDeviceToken(@Nullable String email, @Nullable String userId, @Nullable String authToken, @NonNull String applicationName, @NonNull String deviceToken, @Nullable JSONObject dataFields, Map<String, String> deviceAttributes, @Nullable final IterableHelper.SuccessHandler successHandler, @Nullable final IterableHelper.FailureHandler failureHandler) {
627627
Context context = authProvider.getContext();
628628
JSONObject requestJSON = new JSONObject();
629629
try {
@@ -633,7 +633,7 @@ protected void registerDeviceToken(@Nullable String email, @Nullable String user
633633
dataFields = new JSONObject();
634634
}
635635

636-
for (HashMap.Entry<String, String> entry : deviceAttributes.entrySet()) {
636+
for (Map.Entry<String, String> entry : deviceAttributes.entrySet()) {
637637
dataFields.put(entry.getKey(), entry.getValue());
638638
}
639639

iterableapi/src/main/java/com/iterable/iterableapi/IterableHelper.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
import androidx.annotation.NonNull;
55
import androidx.annotation.Nullable;
66

7+
import org.json.JSONException;
78
import org.json.JSONObject;
89

9-
import java.util.Map;
10-
1110
/**
1211
* Created by David Truong dt@iterable.com
1312
*/
@@ -38,7 +37,17 @@ public interface SuccessAuthHandler {
3837
}
3938

4039
class IterableResponse {
41-
static final JSONObject setEmailLocalSuccessResponse = new JSONObject(Map.of("message", "setEmail was completed locally."));
42-
43-
static final JSONObject setReadLocalSuccessResponse = new JSONObject(Map.of("message", "setRead was completed locally."));
40+
static final JSONObject setEmailLocalSuccessResponse;
41+
static final JSONObject setReadLocalSuccessResponse;
42+
43+
static {
44+
JSONObject emailResponse = new JSONObject();
45+
JSONObject readResponse = new JSONObject();
46+
try {
47+
emailResponse.put("message", "setEmail was completed locally.");
48+
readResponse.put("message", "setRead was completed locally.");
49+
} catch (JSONException ignored) {}
50+
setEmailLocalSuccessResponse = emailResponse;
51+
setReadLocalSuccessResponse = readResponse;
52+
}
4453
}

0 commit comments

Comments
 (0)