Search code examples
androidfirebasefirebase-cloud-messaging

How to send one to one message using Firebase Messaging


I have been trying to read the official docs and guides about how to send message from one device to another. I have saved registration token of both devices in the Real Time Database, thus I have the registration token of another device. I have tried the following way to send the message

RemoteMessage message = new RemoteMessage.Builder(getRegistrationToken())
                    .setMessageId(incrementIdAndGet())
                    .addData("message", "Hello")
                    .build();
FirebaseMessaging.getInstance().send(message);

However this is not working. The other device doesn't receive any message. I am not even sure, if I can use upstream message sending to conduct device to device communication.

PS: I just want to know if device-to-device messaging is possible using FCM? If yes, then is the code I used have some issue? If yes, then what is the correct way.

Update:
My question was to ask whether device to device messaging without using any separate server other than firebase could messaging is possible or not, if yes than how, since there's no documentation about it. I do not understand what is left to explain here? Anyways I got the answer and will update it as an answer once the question gets reopened.


Solution

  • Warning There is a very important reason why we don't mention this approach anywhere. This exposes your server key in the APK that you put on every client device. It can (and thus will) be taken from there and may lead to abuse of your project. I highly recommend against taking this approach, except for apps that you only put on your own devices. – Frank van Puffelen

    Ok, so the answer by Frank was correct that Firebase does not natively support device to device messaging. However there's one loophole in that. The Firebase server doesn't identify whether you have send the request from an actual server or are you doing it from your device.

    So all you have to do is send a Post Request to Firebase's messaging server along with the Server Key. Just keep this in mind that the server key is not supposed to be on the device, but there's no other option if you want device-to-device messaging using Firebase Messaging.

    I am using OkHTTP instead of default way of calling the Rest API. The code is something like this -

    public static final String FCM_MESSAGE_URL = "https://fcm.googleapis.com/fcm/send";
    OkHttpClient mClient = new OkHttpClient();
    public void sendMessage(final JSONArray recipients, final String title, final String body, final String icon, final String message) {
    
            new AsyncTask<String, String, String>() {
                @Override
                protected String doInBackground(String... params) {
                    try {
                        JSONObject root = new JSONObject();
                        JSONObject notification = new JSONObject();
                        notification.put("body", body);
                        notification.put("title", title);
                        notification.put("icon", icon);
    
                        JSONObject data = new JSONObject();
                        data.put("message", message);
                        root.put("notification", notification);
                        root.put("data", data);
                        root.put("registration_ids", recipients);
    
                        String result = postToFCM(root.toString());
                        Log.d(TAG, "Result: " + result);
                        return result;
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                    return null;
                }
    
                @Override
                protected void onPostExecute(String result) {
                    try {
                        JSONObject resultJson = new JSONObject(result);
                        int success, failure;
                        success = resultJson.getInt("success");
                        failure = resultJson.getInt("failure");
                        Toast.makeText(getCurrentActivity(), "Message Success: " + success + "Message Failed: " + failure, Toast.LENGTH_LONG).show();
                    } catch (JSONException e) {
                        e.printStackTrace();
                        Toast.makeText(getCurrentActivity(), "Message Failed, Unknown error occurred.", Toast.LENGTH_LONG).show();
                    }
                }
            }.execute();
        }
    
    String postToFCM(String bodyString) throws IOException {
            RequestBody body = RequestBody.create(JSON, bodyString);
            Request request = new Request.Builder()
                    .url(FCM_MESSAGE_URL)
                    .post(body)
                    .addHeader("Authorization", "key=" + SERVER_KEY)
                    .build();
            Response response = mClient.newCall(request).execute();
            return response.body().string();
        }
    

    I hope Firebase will come with a better solution in future. But till then, I think this is the only way. The other way would be to send topic message or group messaging. But that was not in the scope of the question.

    Update:
    The JSONArray is defined like this -

    JSONArray regArray = new JSONArray(regIds);
    

    regIds is a String array of registration ids, you want to send this message to. Keep in mind that the registration ids must always be in an array, even if you want it to send to a single recipient.