1
1
/*
2
- * Copyright (c) 2020 - 2025 Ping Identity. All rights reserved.
2
+ * Copyright (c) 2020 - 2025 Ping Identity Corporation . All rights reserved.
3
3
*
4
4
* This software may be modified and distributed under the terms
5
5
* of the MIT license. See the LICENSE file for details.
21
21
import org .forgerock .android .auth .exception .MechanismCreationException ;
22
22
import org .forgerock .android .auth .exception .MechanismParsingException ;
23
23
import org .forgerock .android .auth .exception .MechanismPolicyViolationException ;
24
+ import org .forgerock .android .auth .exception .MechanismUpdatePushTokenException ;
25
+ import org .forgerock .android .auth .exception .PushMechanismException ;
24
26
import org .forgerock .android .auth .policy .FRAPolicy ;
25
27
28
+ import java .util .ArrayList ;
26
29
import java .util .Collections ;
27
30
import java .util .List ;
28
31
import java .util .Map ;
32
+ import java .util .concurrent .atomic .AtomicInteger ;
29
33
30
34
class AuthenticatorManager {
31
35
32
36
/** The Storage client. */
33
37
private StorageClient storageClient ;
34
- /** The FCM Device token. */
35
- private String deviceToken ;
36
38
/** The Application Context. */
37
39
private Context context ;
38
40
/** The Oath Factory responsible to build Oath mechanisms. */
@@ -43,17 +45,19 @@ class AuthenticatorManager {
43
45
private NotificationFactory notificationFactory ;
44
46
/** The Policy Evaluator is used to enforce policy compliance. */
45
47
private FRAPolicyEvaluator policyEvaluator ;
48
+ /** The Device token manager is used to manage the device token. **/
49
+ private PushDeviceTokenManager pushDeviceTokenManager ;
46
50
47
51
private static final String TAG = AuthenticatorManager .class .getSimpleName ();
48
52
49
53
AuthenticatorManager (Context context , StorageClient storageClient , FRAPolicyEvaluator policyEvaluator ,
50
54
String deviceToken ) {
51
55
this .context = context ;
52
56
this .storageClient = storageClient ;
53
- this .deviceToken = deviceToken ;
54
57
this .policyEvaluator = policyEvaluator ;
55
58
this .oathFactory = new OathFactory (context , storageClient );
56
- this .pushFactory = new PushFactory (context , storageClient , deviceToken );
59
+ this .pushDeviceTokenManager = new PushDeviceTokenManager (context , storageClient , deviceToken );
60
+ this .pushFactory = new PushFactory (context , storageClient , pushDeviceTokenManager );
57
61
this .notificationFactory = new NotificationFactory (storageClient );
58
62
59
63
OathCodeGenerator .getInstance (storageClient );
@@ -63,7 +67,7 @@ class AuthenticatorManager {
63
67
void createMechanismFromUri (String uri , FRAListener <Mechanism > listener ) {
64
68
Logger .debug (TAG , "Creating new mechanism from URI: %s" , uri );
65
69
if (uri .startsWith (Mechanism .PUSH )) {
66
- if (deviceToken != null ) {
70
+ if (pushDeviceTokenManager . getDeviceTokenId () != null ) {
67
71
pushFactory .createFromUri (uri , listener );
68
72
} else {
69
73
Logger .warn (TAG , "Attempt to add a Push mechanism has failed. " +
@@ -251,25 +255,81 @@ PushNotification getNotificationByMessageId(String messageId) {
251
255
return storageClient .getNotificationByMessageId (messageId );
252
256
}
253
257
254
- void registerForRemoteNotifications (String newDeviceToken ) throws AuthenticatorException {
255
- if (this .deviceToken == null ) {
256
- this .deviceToken = newDeviceToken ;
257
- this .pushFactory = new PushFactory (context , storageClient , newDeviceToken );
258
- this .notificationFactory = new NotificationFactory (storageClient );
259
- PushResponder .getInstance (storageClient );
258
+ void registerForRemoteNotifications (String deviceToken ) throws AuthenticatorException {
259
+ if (this .pushDeviceTokenManager .getDeviceTokenId () == null ) {
260
+ this .pushDeviceTokenManager .setDeviceToken (deviceToken );
260
261
} else {
261
- if (this .deviceToken . equals (newDeviceToken )) {
262
+ if (this .pushDeviceTokenManager . getDeviceTokenId (). equals (deviceToken )) {
262
263
Logger .warn (TAG , "The SDK was already initialized with this device token: %s" ,
263
- newDeviceToken );
264
+ deviceToken );
264
265
throw new AuthenticatorException ("The SDK was already initialized with the FCM device token." );
265
266
} else {
266
267
Logger .warn (TAG , "The SDK was initialized with a different deviceToken: %s, however a new " +
267
- "device token (%s) was received." , this .deviceToken , newDeviceToken );
268
- throw new AuthenticatorException ("The SDK was initialized with a different deviceToken." );
268
+ "device token (%s) was received." , this .pushDeviceTokenManager .getDeviceTokenId (), deviceToken );
269
+ throw new AuthenticatorException ("The SDK was initialized with a different deviceToken. " +
270
+ "Use FRAClient#updateDeviceToken method to update the device token." );
269
271
}
270
272
}
271
273
}
272
274
275
+ void updateDeviceToken (String newDeviceToken , FRAListener <Void > listener ) {
276
+ Logger .debug (TAG , "Updating FCM device token for all Push mechanisms. New token: %s" , newDeviceToken );
277
+
278
+ // Get all push mechanisms
279
+ List <PushMechanism > pushMechanismList = getAllPushMechanisms ();
280
+
281
+ // If no push mechanisms found, return success
282
+ if (pushMechanismList .isEmpty ()) {
283
+ Logger .debug (TAG , "No push mechanisms found. Skipping device token update." );
284
+ listener .onException (new PushMechanismException ("Failed to retrieve PushMechanism objects." ));
285
+ return ;
286
+ }
287
+
288
+ // Update device token for each push mechanism
289
+ final int totalMechanisms = pushMechanismList .size ();
290
+ final AtomicInteger completedMechanisms = new AtomicInteger (0 );
291
+ final List <PushMechanism > failedMechanisms = new ArrayList <>();
292
+ for (PushMechanism pushMechanism : pushMechanismList ) {
293
+ pushDeviceTokenManager .updateDeviceToken (newDeviceToken , pushMechanism , new FRAListener <Void >() {
294
+ @ Override
295
+ public void onSuccess (Void result ) {
296
+ Logger .debug (TAG , "FCM device token for mechanism %s updated successfully." , pushMechanism .getMechanismUID ());
297
+ if (completedMechanisms .incrementAndGet () == totalMechanisms ) {
298
+ if (failedMechanisms .isEmpty ()) {
299
+ // All mechanisms have completed successfully
300
+ listener .onSuccess (null );
301
+ } else {
302
+ // If any mechanism failed, we report the list of failed mechanisms.
303
+ listener .onException (new MechanismUpdatePushTokenException (
304
+ "Error updating FCM device token for some mechanisms." , failedMechanisms ));
305
+ }
306
+ }
307
+ }
308
+
309
+ @ Override
310
+ public void onException (Exception e ) {
311
+ Logger .warn (TAG , "Error updating FCM device token for mechanism %s." , pushMechanism .getMechanismUID (), e );
312
+ failedMechanisms .add (pushMechanism );
313
+ if (completedMechanisms .incrementAndGet () == totalMechanisms ) {
314
+ // All mechanisms have completed, but some failed
315
+ listener .onException (new MechanismUpdatePushTokenException (
316
+ "Error updating FCM device token for some mechanisms." , failedMechanisms ));
317
+ }
318
+ }
319
+ });
320
+ }
321
+ }
322
+
323
+ void updateDeviceTokenForMechanism (String newDeviceToken , PushMechanism pushMechanism , FRAListener <Void > listener ) {
324
+ Logger .debug (TAG , "Updating FCM device token for mechanism %s. New token: %s" , pushMechanism .getMechanismUID (), newDeviceToken );
325
+ pushDeviceTokenManager .updateDeviceToken (newDeviceToken , pushMechanism , listener );
326
+ }
327
+
328
+ PushDeviceToken getPushDeviceToken () {
329
+ Logger .debug (TAG , "Retrieving FCM device token from StorageClient." );
330
+ return pushDeviceTokenManager .getPushDeviceToken ();
331
+ }
332
+
273
333
PushNotification handleMessage (RemoteMessage message )
274
334
throws InvalidNotificationException {
275
335
Logger .debug (TAG , "Processing FCM remote message." );
@@ -407,5 +467,20 @@ void setNotificationFactory(NotificationFactory notificationFactory) {
407
467
this .notificationFactory = notificationFactory ;
408
468
}
409
469
470
+ @ VisibleForTesting
471
+ List <PushMechanism > getAllPushMechanisms () {
472
+ List <PushMechanism > pushMechanismList = new ArrayList <>();
473
+ List <Account > accountList = getAllAccounts ();
474
+ for (Account account : accountList ) {
475
+ List <Mechanism > mechanismList = account .getMechanisms ();
476
+ for (Mechanism mechanism : mechanismList ) {
477
+ if (mechanism .getType ().equals (Mechanism .PUSH )) {
478
+ pushMechanismList .add ((PushMechanism ) mechanism );
479
+ }
480
+ }
481
+ }
482
+ return pushMechanismList ;
483
+ }
484
+
410
485
}
411
486
0 commit comments