Skip to content Skip to sidebar Skip to footer

Google Cloud Messaging (gcm) With Local Device Groups On Android Gives Http Error Code 401

I am trying to get Google Cloud Messaging (GCM) to work on Android with local device groups as described on https://developers.google.com/cloud-messaging/android/client-device-grou

Solution 1:

Found the trick: you are using a google account to take the id_token, you need to use EXACTLY the email as notification_key_name. So if you are using foo@gmail.com, you need to use this address as notification_key_name.

Solution 2:

Here's the recap, based on the correct answer by greywolf82. The correct code should follow these principles (error handling etc. has been stripped):

///////////////////////////////////////////////////////////////////////////
// Working example on how to create a locally (client-side) managed      //
// device group for Google Cloud Messaging.                              //
//                                                                       //
// Thanks to greywolf82 for adding the final piece.                      //
// Please vote on his answer. Thank you!                                 //
///////////////////////////////////////////////////////////////////////////

// Get token:
String account = ACCOUNT_NAME; // E.g. "johndoe@gmail.com"
String scope = "audience:server:client_id:" + WEB_APPLICATION_CLIENT_ID;
String idToken = GoogleAuthUtil.getToken(context, account, scope);

// Get registration id:
InstanceID instanceID = InstanceID.getInstance(this);
String registration_id = instanceID.getToken(
        getString(R.string.gcm_defaultSenderId),
        GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);

// Set up HTTP connection:
URL url = new URL("https://android.googleapis.com/gcm/googlenotification");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("project_id", NUMERICAL_PROJECT_ID);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.connect();

JSONObject requestBody = new JSONObject();
requestBody.put("operation", "add");
requestBody.put("notification_key_name", ACCOUNT_NAME); // You *must* use the email!
requestBody.put("registration_ids",
    new JSONArray(Arrays.asList(new String[]{registration_id})));
requestBody.put("id_token", idToken);

// Submit request body
OutputStream os = connection.getOutputStream();
os.write(requestBody.toString().getBytes("UTF-8"));
os.close();

// connection.getResponseCode() is now 200  :-)
// Now read the server response contents from connection.getInputStream()

The submitted JSON to https://android.googleapis.com/gcm/notification looks something like this:

{
  "operation": "add",
  "notification_key_name": "johndoe@gmail.com",
  "registration_ids": ["very long string here"],
  "id_token": "another very long string"
}

The response contents is:

{"notification_key":"your notification key is here --- voilá!"}

Solution 3:

This is how it is done using the SERVER_API_KEY (not best practice, but the best I have arrived at yet):

First, get an application instance id:

InstanceIDinstanceID= InstanceID.getInstance(this);
Stringtoken= instanceID.getToken(getString(R.string.gcm_defaultSenderId),
    GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);

// Variable "token" now has the instance id. :-)

Next, get the messaging id token:

Stringaccount= ACCOUNT_NAME; // E.g. "johndoe@google.com"Stringscope="audience:server:client_id:" + WEB_APPLICATION_CLIENT_ID;
StringidToken= GoogleAuthUtil.getToken(context, account, scope);

// Variable "idToken" now hods the messaging id token. :-)

Now to the magic part. Create a new device group with the instance Id as a member:

URL url = newURL("https://gcm-http.googleapis.com/gcm/notification"); // <-- Use this URL!HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("Authorization", "key=" + SERVER_API_KEY); // <--- Auth!
connection.setRequestProperty("project_id", NUMERICAL_PROJECT_ID);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.connect();

JSONObject requestBody = newJSONObject();
requestBody.put("operation", "create");               // <--- Not "add"
requestBody.put("notification_key_name", "foobar");
requestBody.put("registration_ids", 
    newJSONArray(Arrays.asList(newString[]{token}))); // <--- Instance Id token here!
requestBody.put("id_token", idToken);

OutputStream os = connection.getOutputStream();
os.write(requestBody.toString().getBytes("UTF-8"));
os.close();

InputStream is = connection.getInputStream();
String responseString = newScanner(is, "UTF-8").useDelimiter("\\A").next();
is.close();

JSONObject response = newJSONObject(responseString);
Log.d(TAG, "Server response:\n" + response.toString(4));
String notificationKey = response.getString("notification_key");

At this point, the variable "notificationKey" holds the notification key which is the recipient that must be used when sending messages to this device group.

Send a message like this (use the "notificationKey" value as parameter as receipient from above):

privatevoidsendMessage(String recipient) throws IOException, JSONException {

    Log.i(TAG, "Sending message to: " + recipient);
    URL url = newURL("https://gcm-http.googleapis.com/gcm/send");
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("POST");
    connection.setDoOutput(true);
    connection.setDoInput(true);

    Log.d(TAG, "Opening connection to " + url.toString());

    connection.setRequestProperty("Authorization", "key=" + SERVER_API_KEY);
    connection.setRequestProperty("Content-Type", "application/json");
    connection.setRequestProperty("Accept", "application/json");
    connection.connect();

    JSONObject requestBody = newJSONObject();
    requestBody.put("to", recipient);
    JSONObject requestData = newJSONObject();
    requestData.put("hello", "Hello World!");
    requestData.put("hellodata", "42");
    requestBody.put("data", requestData);

    Log.d(TAG, "Request body:\n" + requestBody.toString(4));

    OutputStream os = connection.getOutputStream();
    os.write(requestBody.toString().getBytes("UTF-8"));
    os.close();

    Log.d(TAG, "Response code: " + connection.getResponseCode());

    InputStream is = connection.getInputStream();
    String responseString = newScanner(is, "UTF-8").useDelimiter("\\A").next();
    is.close();
}

I have not succeeded in doing this without using the server API key. It might be worthwhile to investigate with different Authorization header values on the https://http.googleapis.com/gcm/googlenotification endpoint.

Hopefully this makes sense. Have fun. :-)

Post a Comment for "Google Cloud Messaging (gcm) With Local Device Groups On Android Gives Http Error Code 401"