Search code examples
androidpush-notificationaws-sdkandroid-notificationsaws-sdk-android

InvalidConfigurationException: Identity pool isn't set up for SNS using AmazonCognitoSync service


I have been trying to use AWS SDKs for Push Notifications. But I am getting errors. Tried to find a solution, but can't find much support for this.

iOS & Web push notifications are working fine

What all is already setup & done:

  • AWS back-end & console setting in place.
  • Identity Pool Id & other keys in place.
  • ARN topic in place.

Android side:

  • AWS SDK dependencies:

    implementation 'com.amazonaws:aws-android-sdk-core:2.16.8'
    implementation 'com.amazonaws:aws-android-sdk-cognito:2.6.23'
    implementation 'com.amazonaws:aws-android-sdk-s3:2.15.1'
    implementation 'com.amazonaws:aws-android-sdk-ddb:2.2.0'
    implementation ('com.amazonaws:aws-android-sdk-mobile-client:2.16.8') { transitive = true; }
    

minSdkVersion 21

targetSdkVersion 29

  • Inside onCreate:

    CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
                    getApplicationContext(),
                    "My Pool Id here", // Identity pool ID
                    Regions.US_EAST_1 // Region
            );
    
    CognitoSyncManager client = new CognitoSyncManager(
                            LoginActivateActivity.this,
                            Regions.US_EAST_1,
                            credentialsProvider);
    
                String registrationId = "MY_FCM_DEVICE_TOKEN"; **Instead of GCM ID, I am passing my unique FCM device token here. I searched, & it seems that wherever GCM is required, it is being replaced by FCM.**
                try {
                    client.registerDevice("GCM", registrationId);
                } catch (RegistrationFailedException rfe) {
                    Log.e("TAG", "Failed to register device for silent sync", rfe);
                } catch (AmazonClientException ace) {
                    Log.e("TAG", "An unknown error caused registration for silent sync to fail", ace);
                }
    
                Dataset trackedDataset = client.openOrCreateDataset("My Topic here");
                if (client.isDeviceRegistered()) {
                    try {
                        trackedDataset.subscribe();
                    } catch (SubscribeFailedException sfe) {
                        Log.e("TAG", "Failed to subscribe to datasets", sfe);
                    } catch (AmazonClientException ace) {
                        Log.e("TAG", "An unknown error caused the subscription to fail", ace);
                    }
                }
    

Error I am getting on client.registerDevice("GCM", registrationId);

Caused by: com.amazonaws.services.cognitosync.model.InvalidConfigurationException: Identity pool isn't set up for SNS (Service: AmazonCognitoSync; Status Code: 400; Error Code: InvalidConfigurationException; Request ID: a858aaa2-**************************)

Note:

I tried using Amplify libraries, but even that didn't work. Also, at iOS & Web end they are using AWS SDK. So I am also bound to use the same. This is not even a device specific error.

All I need to do is setup my project to get push notifications. But I am stuck at the initial step. Not able to create an endpoint for Android device.


Solution

  • I actually found the solution to the issue, thanks to a friend who shared this link:

    https://aws.amazon.com/premiumsupport/knowledge-center/create-android-push-messaging-sns/

    This Youtube video also helped a lot:

    https://www.youtube.com/watch?v=9QSO3ghSUNk&list=WL&index=3

    Edited code

    private void registerWithSNS() {
    
            CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
                    getApplicationContext(),
                    "Your Identity Pool ID",
                    Regions.US_EAST_1 // Region
            );
    
            client = new AmazonSNSClient(credentialsProvider);
    
            String endpointArn = retrieveEndpointArn();
            String token = "Your FCM Registration ID generated for the device";
    
            boolean updateNeeded = false;
            boolean createNeeded = (null == endpointArn || "".equalsIgnoreCase(endpointArn));
    
            if (createNeeded) {
                // No platform endpoint ARN is stored; need to call createEndpoint.
                endpointArn = createEndpoint(token);
                createNeeded = false;
            }
    
            System.out.println("Retrieving platform endpoint data...");
            // Look up the platform endpoint and make sure the data in it is current, even if
            // it was just created.
            try {
                GetEndpointAttributesRequest geaReq =
                        new GetEndpointAttributesRequest()
                                .withEndpointArn(endpointArn);
                GetEndpointAttributesResult geaRes =
                        client.getEndpointAttributes(geaReq);
    
                updateNeeded = !geaRes.getAttributes().get("Token").equals(token)
                        || !geaRes.getAttributes().get("Enabled").equalsIgnoreCase("true");
    
            } catch (NotFoundException nfe) {
                // We had a stored ARN, but the platform endpoint associated with it
                // disappeared. Recreate it.
                createNeeded = true;
            } catch (AmazonClientException e) {
                createNeeded = true;
            }
    
            if (createNeeded) {
                createEndpoint(token);
            }
    
            System.out.println("updateNeeded = " + updateNeeded);
    
            if (updateNeeded) {
                // The platform endpoint is out of sync with the current data;
                // update the token and enable it.
                System.out.println("Updating platform endpoint " + endpointArn);
                Map attribs = new HashMap();
                attribs.put("Token", token);
                attribs.put("Enabled", "true");
                SetEndpointAttributesRequest saeReq =
                        new SetEndpointAttributesRequest()
                                .withEndpointArn(endpointArn)
                                .withAttributes(attribs);
                client.setEndpointAttributes(saeReq);
            }
        }
    
        /**
         * @return never null
         * */
        private String createEndpoint(String token) {
    
            String endpointArn = null;
            try {
                System.out.println("Creating platform endpoint with token " + token);
                CreatePlatformEndpointRequest cpeReq =
                        new CreatePlatformEndpointRequest()
                                .withPlatformApplicationArn("Your Platform ARN. This you get from AWS Console. Unique for all devices for a platform.")
                                .withToken(token);
                CreatePlatformEndpointResult cpeRes = client
                        .createPlatformEndpoint(cpeReq);
                endpointArn = cpeRes.getEndpointArn();
            } catch (InvalidParameterException ipe) {
                String message = ipe.getErrorMessage();
                System.out.println("Exception message: " + message);
                Pattern p = Pattern
                        .compile(".*Endpoint (arn:aws:sns[^ ]+) already exists " +
                                "with the same [Tt]oken.*");
                Matcher m = p.matcher(message);
                if (m.matches()) {
                    // The platform endpoint already exists for this token, but with
                    // additional custom data that
                    // createEndpoint doesn't want to overwrite. Just use the
                    // existing platform endpoint.
                    endpointArn = m.group(1);
                } else {
                    // Rethrow the exception, the input is actually bad.
                    throw ipe;
                }
            }
            storeEndpointArn(endpointArn);
            return endpointArn;
        }
    
        /**
         * @return the ARN the app was registered under previously, or null if no
         *         platform endpoint ARN is stored.
         */
        private String retrieveEndpointArn() {
            // Retrieve the platform endpoint ARN from permanent storage,
            // or return null if null is stored.
            return endpointArn;
        }
    
        /**
         * Stores the platform endpoint ARN in permanent storage for lookup next time.
         * */
        private void storeEndpointArn(String endpointArn) {
            // Write the platform endpoint ARN to permanent storage.
            UserSession.getSession(LoginActivateActivity.this).setARN(endpointArn); //Your platform endpoint ARN. This is unique for each device, but changes when 
        }
    

    Once an endpoint is created for the device, you need to store the endpointArn & FCM registration ID to your DB on server-side. Rest of the code will be your FCM implementation code for receiving notifications.

    Hope this helps someone