11package com .iterable .iterableapi ;
22
33import android .util .Base64 ;
4-
54import androidx .annotation .VisibleForTesting ;
65
76import org .json .JSONException ;
@@ -20,23 +19,46 @@ public class IterableAuthManager {
2019 private final IterableApi api ;
2120 private final IterableAuthHandler authHandler ;
2221 private final long expiringAuthTokenRefreshPeriod ;
23- private final long scheduledRefreshPeriod = 10000 ;
2422 @ VisibleForTesting
2523 Timer timer ;
2624 private boolean hasFailedPriorAuth ;
2725 private boolean pendingAuth ;
2826 private boolean requiresAuthRefresh ;
27+ RetryPolicy authRetryPolicy ;
28+ boolean pauseAuthRetry ;
29+ int retryCount ;
30+ private boolean isLastAuthTokenValid ;
31+ private boolean isTimerScheduled ;
2932
3033 private final ExecutorService executor = Executors .newSingleThreadExecutor ();
3134
32- IterableAuthManager (IterableApi api , IterableAuthHandler authHandler , long expiringAuthTokenRefreshPeriod ) {
35+ IterableAuthManager (IterableApi api , IterableAuthHandler authHandler , RetryPolicy authRetryPolicy , long expiringAuthTokenRefreshPeriod ) {
3336 this .api = api ;
3437 this .authHandler = authHandler ;
38+ this .authRetryPolicy = authRetryPolicy ;
3539 this .expiringAuthTokenRefreshPeriod = expiringAuthTokenRefreshPeriod ;
3640 }
3741
3842 public synchronized void requestNewAuthToken (boolean hasFailedPriorAuth ) {
39- requestNewAuthToken (hasFailedPriorAuth , null );
43+ requestNewAuthToken (hasFailedPriorAuth , null , true );
44+ }
45+
46+ public void pauseAuthRetries (boolean pauseRetry ) {
47+ pauseAuthRetry = pauseRetry ;
48+ resetRetryCount ();
49+ }
50+
51+ void reset () {
52+ clearRefreshTimer ();
53+ setIsLastAuthTokenValid (false );
54+ }
55+
56+ void setIsLastAuthTokenValid (boolean isValid ) {
57+ isLastAuthTokenValid = isValid ;
58+ }
59+
60+ void resetRetryCount () {
61+ retryCount = 0 ;
4062 }
4163
4264 private void handleSuccessForAuthToken (String authToken , IterableHelper .SuccessHandler successCallback ) {
@@ -51,7 +73,12 @@ private void handleSuccessForAuthToken(String authToken, IterableHelper.SuccessH
5173
5274 public synchronized void requestNewAuthToken (
5375 boolean hasFailedPriorAuth ,
54- final IterableHelper .SuccessHandler successCallback ) {
76+ final IterableHelper .SuccessHandler successCallback ,
77+ boolean shouldIgnoreRetryPolicy ) {
78+ if ((!shouldIgnoreRetryPolicy && pauseAuthRetry ) || (retryCount >= authRetryPolicy .maxRetry && !shouldIgnoreRetryPolicy )) {
79+ return ;
80+ }
81+
5582 if (authHandler != null ) {
5683 if (!pendingAuth ) {
5784 if (!(this .hasFailedPriorAuth && hasFailedPriorAuth )) {
@@ -62,9 +89,17 @@ public synchronized void requestNewAuthToken(
6289 @ Override
6390 public void run () {
6491 try {
92+ if (isLastAuthTokenValid && !shouldIgnoreRetryPolicy ) {
93+ // if some JWT retry had valid token it will not fetch the auth token again from developer function
94+ handleAuthTokenSuccess (IterableApi .getInstance ().getAuthToken (), successCallback );
95+ return ;
96+ }
6597 final String authToken = authHandler .onAuthTokenRequested ();
98+ pendingAuth = false ;
99+ retryCount ++;
66100 handleAuthTokenSuccess (authToken , successCallback );
67101 } catch (final Exception e ) {
102+ retryCount ++;
68103 handleAuthTokenFailure (e );
69104 }
70105 }
@@ -89,12 +124,11 @@ private void handleAuthTokenSuccess(String authToken, IterableHelper.SuccessHand
89124 } else {
90125 IterableLogger .w (TAG , "Auth token received as null. Calling the handler in 10 seconds" );
91126 //TODO: Make this time configurable and in sync with SDK initialization flow for auth null scenario
92- scheduleAuthTokenRefresh (scheduledRefreshPeriod );
127+ scheduleAuthTokenRefresh (getNextRetryInterval (), false , null );
93128 authHandler .onTokenRegistrationFailed (new Throwable ("Auth token null" ));
94129 return ;
95130 }
96131 IterableApi .getInstance ().setAuthToken (authToken );
97- pendingAuth = false ;
98132 reSyncAuth ();
99133 authHandler .onTokenRegistrationSuccessful (authToken );
100134 }
@@ -103,7 +137,7 @@ private void handleAuthTokenFailure(Throwable throwable) {
103137 IterableLogger .e (TAG , "Error while requesting Auth Token" , throwable );
104138 authHandler .onTokenRegistrationFailed (throwable );
105139 pendingAuth = false ;
106- reSyncAuth ( );
140+ scheduleAuthTokenRefresh ( getNextRetryInterval (), false , null );
107141 }
108142
109143 public void queueExpirationRefresh (String encodedJWT ) {
@@ -112,15 +146,15 @@ public void queueExpirationRefresh(String encodedJWT) {
112146 long expirationTimeSeconds = decodedExpiration (encodedJWT );
113147 long triggerExpirationRefreshTime = expirationTimeSeconds * 1000L - expiringAuthTokenRefreshPeriod - IterableUtil .currentTimeMillis ();
114148 if (triggerExpirationRefreshTime > 0 ) {
115- scheduleAuthTokenRefresh (triggerExpirationRefreshTime );
149+ scheduleAuthTokenRefresh (triggerExpirationRefreshTime , true , null );
116150 } else {
117151 IterableLogger .w (TAG , "The expiringAuthTokenRefreshPeriod has already passed for the current JWT" );
118152 }
119153 } catch (Exception e ) {
120154 IterableLogger .e (TAG , "Error while parsing JWT for the expiration" , e );
121155 authHandler .onTokenRegistrationFailed (new Throwable ("Auth token decode failure. Scheduling auth token refresh in 10 seconds..." ));
122156 //TODO: Sync with configured time duration once feature is available.
123- scheduleAuthTokenRefresh (scheduledRefreshPeriod );
157+ scheduleAuthTokenRefresh (getNextRetryInterval (), false , null );
124158 }
125159 }
126160
@@ -131,28 +165,57 @@ void resetFailedAuth() {
131165 void reSyncAuth () {
132166 if (requiresAuthRefresh ) {
133167 requiresAuthRefresh = false ;
134- requestNewAuthToken ( false );
168+ scheduleAuthTokenRefresh ( getNextRetryInterval (), false , null );
135169 }
136170 }
137171
138- void scheduleAuthTokenRefresh (long timeDuration ) {
139- timer = new Timer (true );
172+ long getNextRetryInterval () {
173+ long nextRetryInterval = authRetryPolicy .retryInterval ;
174+ if (authRetryPolicy .retryBackoff == RetryPolicy .Type .EXPONENTIAL ) {
175+ nextRetryInterval *= Math .pow (IterableConstants .EXPONENTIAL_FACTOR , retryCount - 1 ); // Exponential backoff
176+ }
177+
178+ return nextRetryInterval ;
179+ }
180+
181+ void scheduleAuthTokenRefresh (long timeDuration , boolean isScheduledRefresh , final IterableHelper .SuccessHandler successCallback ) {
182+ if ((pauseAuthRetry && !isScheduledRefresh ) || isTimerScheduled ) {
183+ // we only stop schedule token refresh if it is called from retry (in case of failure). The normal auth token refresh schedule would work
184+ return ;
185+ }
186+ if (timer == null ) {
187+ timer = new Timer (true );
188+ }
140189 try {
141190 timer .schedule (new TimerTask () {
142191 @ Override
143192 public void run () {
144193 if (api .getEmail () != null || api .getUserId () != null ) {
145- api .getAuthManager ().requestNewAuthToken (false );
194+ api .getAuthManager ().requestNewAuthToken (false , successCallback , isScheduledRefresh );
146195 } else {
147196 IterableLogger .w (TAG , "Email or userId is not available. Skipping token refresh" );
148197 }
198+ isTimerScheduled = false ;
149199 }
150200 }, timeDuration );
201+ isTimerScheduled = true ;
151202 } catch (Exception e ) {
152203 IterableLogger .e (TAG , "timer exception: " + timer , e );
153204 }
154205 }
155206
207+ private String getEmailOrUserId () {
208+ String email = api .getEmail ();
209+ String userId = api .getUserId ();
210+
211+ if (email != null ) {
212+ return email ;
213+ } else if (userId != null ) {
214+ return userId ;
215+ }
216+ return null ;
217+ }
218+
156219 private static long decodedExpiration (String encodedJWT ) throws Exception {
157220 long exp = 0 ;
158221 String [] split = encodedJWT .split ("\\ ." );
0 commit comments