PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Push Notifications in Android Apps via C2DM

with 6 comments

Seit Android 2.2 gibt es in Android Apps die Möglichkeit auf Push-Nachrichten zu reagieren. Dieser Dienst wird Cloud to Device Messaging oder kurz C2DM genannt.

Die App registriert sich dazu bei den C2DM Servern an und bekommt dafür eine DeviceRegistrationID. Ab dann wird die Verbindung zwischen dem Android Market (neuerdings Google Play genannt) und den Google-Servern genutzt um die Push-Nachrichten auf das Handy zu bekommen.

Die DeviceRegistrationID muss die App dann an unseren Server melden, und alle Nachrichten an diese DeviceRegistrationID kommen dann auf genau diesem Handy an. Sollte das Handy ausgeschaltet sein werden die Nachrichten zugestellt sobald das Handy (und die App) wieder gestartet sind. Falls alle Nachrichten den selben „Collapse Key“ haben wird nur die neueste zugestellt.

Der Server, der eine Nachricht senden möchte, sendet dazu einen POST Request an Googles C2DM Dienst. Der Text der Nachricht ist auf maximal 1024 Byte beschränkt, sollte also nur für kurze „Aufweckpings“ bzw. Nachrichten a la „Es gibt Neuigkeiten vom Typ X, frag beim Server nach“ geeignet sein.

Hier das ganze Verfahren nochmal grafisch dargestellt:

  1. Die App meldet sich beim C2DM Dienst an
  2. Die App bekommt eine Device Registration ID
  3. Die App meldet diese Device Registration ID an den Applikationsserver der diese dann einem Benutzer zuordnet und abspeichert
  4. Wenn auf dem Applikationsserver ein Event passiert über das das Handy benachrichtig werden soll…
  5. … wird diese Nachricht bzw. die Eventbenachrichtung an den C2DM Dienst übergeben
  6. Google sendet dann diese Nachricht an das entsprechende Handy und die App kann auf die Nachricht reagieren

Ich möchte hier den Serverteil vorstellen, der die Nachrichten an Googles Gateway übergibt und die dann in der App ankommen sollen. Dazu benötigen wir einen Google-Account den wir beim C2DM Dienst registrieren. Etwas vorsichtig muss man sein was man dort angibt: Die Role E-Mail ID muss ein Google-Account sein der nicht auf einem Handy registriert ist, am besten legt man dafür also einen separaten Account ein, denn diese E-Mail-Adresse ist im App eingetragen und das Passwort dieses Accounts wird auch im Quelltext auf dem Server abgelegt.

Danach hat man einen Google-Account der Nachrichten an den C2DM Dienst senden kann (anfangs „nur“ 200.000 pro Tag, das kann man aber erhöhen lassen). Für die Authentifizierung am C2DM Dienst benötigen wir einen AuthCode den wir mittels des sogenannten ClientLogin Dienstes bei Google bekommen.

function getGoogleAuthCodeHelper($username, $password, $source='Company-AppName-Version', $service='ac2dm')
{
    // get an authorization token
    $ch = curl_init();
    if(!$ch){
        return false;
    }

    curl_setopt($ch, CURLOPT_URL, "https://www.google.com/accounts/ClientLogin");
    $postFields = "accountType=" . urlencode('HOSTED_OR_GOOGLE')
        . "&Email=" . urlencode($username)
        . "&Passwd=" . urlencode($password)
        . "&source=" . urlencode($source)
        . "&service=" . urlencode($service);
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
    curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

    // for debugging the request
    //curl_setopt($ch, CURLINFO_HEADER_OUT, true); // for debugging the request

    $response = curl_exec($ch);

    //var_dump(curl_getinfo($ch)); //for debugging the request
    //var_dump($response);

    curl_close($ch);

    if (strpos($response, '200 OK') === false) {
        throw new Exception('Error occurred');
    }

    // find the auth code
    preg_match("/(Auth=)([\w|-]+)/", $response, $matches);

    if (!$matches[2]) {
        throw new Exception('Error occurred');
    }

    return $matches[2];
}

Mit Hilfe dieser Funktion können wir uns einen AuthCode besorgen den wir für die eigentliche Sendefunktion benötigen:

function send($message, $authCode, $deviceRegistrationId, $msgType)
{
    $headers = array('Authorization: GoogleLogin auth=' . $authCode);
    $data = array(
        'registration_id' => $deviceRegistrationId,
        'collapse_key'    => $msgType,
        'data.message'    => $message
    );

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, "https://android.apis.google.com/c2dm/send");
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

    $response = curl_exec($ch);
    curl_close($ch);

    // Check the response. If the exact error message is needed it can be parsed here
    $responseArray = preg_split('/=/', $response);
    if (!isset($responseArray[0]) || !isset($responseArray[1]) || strtolower($responseArray[0]) == 'error') {
        throw new Exception('Error occurred');
    }
}

Das war es eigentlich schon, mit diesen beiden Requests kann man Push Notifications an die App senden. Wie genau die App aussieht um die Notifications zu empfangen und anfangs die DeviceRegistrationID zu beantragen möge man sich bei Google nachlesen.

Zusatzinfo: Das ganze ist zu Testzwecken auch mit dem curl-Kommando auf Shellebene machbar:

curl https://www.google.com/accounts/ClientLogin -d Email=**theEmailYouWhitelisted**
-d Passwd=**password** -d accountType=HOSTED_OR_GOOGLE -d source=Google-cURL-Example
-d service=ac2dm

 

curl --header "Authorization: GoogleLogin auth=**authFromRegistrationAbove**"
"https://android.apis.google.com/c2dm/send" -d registration_id=**deviceRegistrationId**
-d "data.message=StringToPass" -d collapse_key=something -k

Written by Michael Kliewe

März 20th, 2012 at 9:56 am

6 Responses to 'Push Notifications in Android Apps via C2DM'

Subscribe to comments with RSS or TrackBack to 'Push Notifications in Android Apps via C2DM'.

  1. Danke, sehr hilfreich, werde in nächster Zeit etwas damit rumspielen und evt. einsetzen!

    Sven

    23 Mrz 12 at 08:50

  2. Hi, cooles Tut. Aber wo muss ich die Code-Schnipsel jetzt eintragen?

    Oliver

    7 Jun 12 at 13:35

  3. Hi,

    kannst du mir da ein Beispiel senden…?

    Bin

    8 Dez 12 at 20:46

  4. C2DM ist outdatet – man soll nun GCM verwenden (http://developer.android.com/google/gcm/index.html).

    Martin

    11 Dez 12 at 13:09

  5. Haha heute sind alle großen Messenger deswegen lahmgelegt gewesen 😀 (oder?)

    Instant

    23 Feb 14 at 00:52

  6. Hallo kurze Frage, wie wird es behandelt, wenn 2 Personen gleiches Gerät nutzen möchten. Gerät ermöglicht Installation nur einer App. Das schließt aber nicht aus, dass auf einer App zwei separate Konten bestehen (1) UserA hat App installiert und seine DeviceRegId ist z.B. AAA. Der bekommt die Mitteilungen auf das Gerät, aufs Konto A 2) die Frau von User A nutzt das gleiche Gerät aber die hat sich neu eingeloggt, somit sollte die Mitteilungen ihres Mannes nicht kriegen (Hat andere DeviceID??) )

    Danke und Grüße

    Sambor

    16 Apr 15 at 15:24

Leave a Reply

You can add images to your comment by clicking here.