Skip to content

Commit eddcc65

Browse files
authored
Merge pull request #635 from Iterable/MOB-6865-Keychain-null-crash-fix
[MOB - 6865] - Keychain null crash fix
2 parents d628203 + 8d5b427 commit eddcc65

File tree

4 files changed

+60
-47
lines changed

4 files changed

+60
-47
lines changed

iterableapi/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ dependencies {
4242
api 'androidx.appcompat:appcompat:1.0.0'
4343
api 'androidx.annotation:annotation:1.0.0'
4444
api 'com.google.firebase:firebase-messaging:20.3.0'
45-
implementation "androidx.security:security-crypto:1.1.0-alpha04"
45+
implementation "androidx.security:security-crypto:1.1.0-alpha06"
4646

4747
testImplementation 'junit:junit:4.12'
4848
testImplementation 'androidx.test:runner:1.3.0'

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

+17-13
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ IterableAuthManager getAuthManager() {
135135
return authManager;
136136
}
137137

138-
@NonNull
138+
@Nullable
139139
IterableKeychain getKeychain() {
140140
if (keychain == null) {
141141
try {
@@ -350,15 +350,25 @@ private String getDeviceId() {
350350
}
351351

352352
private void storeAuthData() {
353-
getKeychain().saveEmail(_email);
354-
getKeychain().saveUserId(_userId);
355-
getKeychain().saveAuthToken(_authToken);
353+
IterableKeychain iterableKeychain = getKeychain();
354+
if (iterableKeychain != null) {
355+
iterableKeychain.saveEmail(_email);
356+
iterableKeychain.saveUserId(_userId);
357+
iterableKeychain.saveAuthToken(_authToken);
358+
} else {
359+
IterableLogger.e(TAG, "Shared preference creation failed. ");
360+
}
356361
}
357362

358363
private void retrieveEmailAndUserId() {
359-
_email = getKeychain().getEmail();
360-
_userId = getKeychain().getUserId();
361-
_authToken = getKeychain().getAuthToken();
364+
IterableKeychain iterableKeychain = getKeychain();
365+
if (iterableKeychain != null) {
366+
_email = iterableKeychain.getEmail();
367+
_userId = iterableKeychain.getUserId();
368+
_authToken = iterableKeychain.getAuthToken();
369+
} else {
370+
IterableLogger.e(TAG, "retrieveEmailAndUserId: Shared preference creation failed. Could not retrieve email/userId");
371+
}
362372

363373
if (config.authHandler != null) {
364374
if (_authToken != null) {
@@ -370,10 +380,6 @@ private void retrieveEmailAndUserId() {
370380
}
371381
}
372382

373-
private void updateSDKVersion() {
374-
375-
}
376-
377383
private class IterableApiAuthProvider implements IterableApiClient.AuthProvider {
378384
@Nullable
379385
@Override
@@ -507,8 +513,6 @@ public static void initialize(@NonNull Context context, @NonNull String apiKey,
507513
sharedInstance.config = new IterableConfig.Builder().build();
508514
}
509515

510-
sharedInstance.updateSDKVersion();
511-
512516
sharedInstance.retrieveEmailAndUserId();
513517

514518
IterableActivityMonitor.getInstance().registerLifecycleCallbacks(context);

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

+6
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ public Builder setAllowedProtocols(@NonNull String[] allowedProtocols) {
237237
return this;
238238
}
239239

240+
/**
241+
* Set whether the SDK should enforce encryption. If set to `true`, the SDK will not use fallback mechanism
242+
* of storing data in un-encrypted shared preferences if encrypted database is not available. Set this to `true`
243+
* if PII confidentiality is a concern for your app.
244+
* @param encryptionEnforced `true` will have the SDK enforce encryption.
245+
*/
240246
public Builder setEncryptionEnforced(boolean encryptionEnforced) {
241247
this.encryptionEnforced = encryptionEnforced;
242248
return this;

iterableapi/src/main/java/com/iterable/iterableapi/IterableKeychain.kt

+36-33
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ class IterableKeychain {
2626
encryptionEnabled = false
2727
sharedPrefs = context.getSharedPreferences(
2828
IterableConstants.SHARED_PREFS_FILE,
29-
Context.MODE_PRIVATE)
29+
Context.MODE_PRIVATE
30+
)
3031
IterableLogger.v(TAG, "SharedPreferences being used")
3132
} else {
3233
// See if EncryptedSharedPreferences can be created successfully
@@ -39,45 +40,42 @@ class IterableKeychain {
3940
encryptedSharedPrefsFileName,
4041
masterKeyAlias,
4142
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
42-
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)
43+
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
44+
)
4345
encryptionEnabled = true
44-
} catch (e: Exception) {
46+
} catch (e: Throwable) {
47+
if (e is Error) {
48+
IterableLogger.e(
49+
TAG,
50+
"EncryptionSharedPreference creation failed with Error. Attempting to continue"
51+
)
52+
}
53+
4554
if (encryptionEnforced) {
46-
//TODO: In memory Or similar solution needs to be implemented in future.
47-
IterableLogger.e(TAG, "Error creating EncryptedSharedPreferences", e)
55+
//TODO: In-memory or similar solution needs to be implemented in the future.
56+
IterableLogger.w(
57+
TAG,
58+
"Encryption is enforced. PII will not be persisted due to EncryptionSharedPreference failure. Email/UserId and Auth token will have to be passed for every app session.",
59+
e
60+
)
4861
throw e.fillInStackTrace()
4962
} else {
5063
sharedPrefs = context.getSharedPreferences(
5164
IterableConstants.SHARED_PREFS_FILE,
52-
Context.MODE_PRIVATE)
65+
Context.MODE_PRIVATE
66+
)
67+
IterableLogger.w(
68+
TAG,
69+
"Using SharedPreference as EncryptionSharedPreference creation failed."
70+
)
5371
encryptionEnabled = false
5472
}
5573
}
5674

5775
//Try to migrate data from SharedPreferences to EncryptedSharedPreferences
58-
if(encryptionEnabled) {
76+
if (encryptionEnabled) {
5977
migrateAuthDataFromSharedPrefsToKeychain(context)
6078
}
61-
62-
}
63-
val masterKeyAlias = MasterKey.Builder(context)
64-
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
65-
.build()
66-
sharedPrefs = try {
67-
EncryptedSharedPreferences.create(
68-
context,
69-
encryptedSharedPrefsFileName,
70-
masterKeyAlias,
71-
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
72-
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
73-
)
74-
} catch (e: Exception) {
75-
if (encryptionEnforced) {
76-
IterableLogger.e(TAG, "Error creating EncryptedSharedPreferences", e)
77-
throw e.fillInStackTrace()
78-
} else {
79-
context.getSharedPreferences(encryptedSharedPrefsFileName, Context.MODE_PRIVATE)
80-
}
8179
}
8280
}
8381

@@ -113,17 +111,20 @@ class IterableKeychain {
113111

114112
@RequiresApi(api = Build.VERSION_CODES.M)
115113
private fun migrateAuthDataFromSharedPrefsToKeychain(context: Context) {
116-
val oldPrefs: SharedPreferences = context.getSharedPreferences(
114+
val oldPrefs: SharedPreferences = context.getSharedPreferences(
117115
IterableConstants.SHARED_PREFS_FILE,
118-
Context.MODE_PRIVATE)
116+
Context.MODE_PRIVATE
117+
)
119118
val sharedPrefsEmail = oldPrefs.getString(IterableConstants.SHARED_PREFS_EMAIL_KEY, null)
120119
val sharedPrefsUserId = oldPrefs.getString(IterableConstants.SHARED_PREFS_USERID_KEY, null)
121-
val sharedPrefsAuthToken = oldPrefs.getString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, null)
120+
val sharedPrefsAuthToken =
121+
oldPrefs.getString(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY, null)
122122
val editor: SharedPreferences.Editor = oldPrefs.edit()
123123
if (getEmail() == null && sharedPrefsEmail != null) {
124124
saveEmail(sharedPrefsEmail)
125125
editor.remove(IterableConstants.SHARED_PREFS_EMAIL_KEY)
126-
IterableLogger.v(TAG,
126+
IterableLogger.v(
127+
TAG,
127128
"UPDATED: migrated email from SharedPreferences to IterableKeychain"
128129
)
129130
} else if (sharedPrefsEmail != null) {
@@ -132,7 +133,8 @@ class IterableKeychain {
132133
if (getUserId() == null && sharedPrefsUserId != null) {
133134
saveUserId(sharedPrefsUserId)
134135
editor.remove(IterableConstants.SHARED_PREFS_USERID_KEY)
135-
IterableLogger.v(TAG,
136+
IterableLogger.v(
137+
TAG,
136138
"UPDATED: migrated userId from SharedPreferences to IterableKeychain"
137139
)
138140
} else if (sharedPrefsUserId != null) {
@@ -141,7 +143,8 @@ class IterableKeychain {
141143
if (getAuthToken() == null && sharedPrefsAuthToken != null) {
142144
saveAuthToken(sharedPrefsAuthToken)
143145
editor.remove(IterableConstants.SHARED_PREFS_AUTH_TOKEN_KEY)
144-
IterableLogger.v(TAG,
146+
IterableLogger.v(
147+
TAG,
145148
"UPDATED: migrated authToken from SharedPreferences to IterableKeychain"
146149
)
147150
} else if (sharedPrefsAuthToken != null) {

0 commit comments

Comments
 (0)