Google Play generates notifications in response to events that occur and can affect an enterprise. EMM solution providers can receive these notifications and act upon them, by providing alerts or other mechanisms for their customer administrators, for example.
This guide tells you how to configure the server infrastructure needed to receive and process EMM push notifications only. Pull notifications do not require the setup detailed in this guide.
In addition to the server setup described in this guide, for push notifications you must also grant the appropriate privileges and perform other configuration tasks in the Google API Console. See Enable EMM push notifications for details.
For more information about Google Cloud Pub/Sub, including examples, see Cloud Pub/Sub documentation.
You can configure your system so that push notifications are sent to a specified HTTPS endpoint, or to a server that waits for notifications to be sent.
About push endpoint configuration
To configure a push endpoint you need a server with a valid SSL certificate. In this example, you create and upload an SSL certificate to a certificate authority (CA), then configure the NGINX server. Finally you compile and run test code to confirm that your setup is correct.
This example shows how to configure an
NGINX server in
reverse proxy mode to connect to the subscriber app (in PushSubscriber.java
)
running on port 8093, using Ubuntu 14.04. Your enterprise might use a different
server, but the sample setup should work, without changes, on all Debian-based
distributions. Other distributions (such as those based on RedHat) are similar,
but the location of the configuration files may be different.
Before you can receive notifications, you must configure an endpoint that meets the following criteria:
You must own the domain and verify your ownership in the Google API Console.
You must be able to run a service on port 443 (SSL).
You must have a CA-signed SSL certificate. Self-signed certificates do not work.
The web server you are running must support webhooks.
Your endpoint does not need to run on Google App Engine (although it can).
Create and upload an SSL certificate
1. Produce a Secure Sockets Layer (SSL) certificate:
myusername@myhost:/tmp$ sudo openssl req -x509 -nodes -days 365 \ -newkey rsa:2048 -keyout cert.key -out cert.crt
This generates the following response. Replace the sample values
(such as push.solarmora.com
and myusername@myhost
) with your actual server
name, company, address, and so on, in the following code. You can use any
subdomain as long as the A
record of this subdomain points to your server.
Generating a 2048 bit RSA private key ........................................................................... .....+++ writing new private key to 'cert.key' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:GB State or Province Name (full name) [Some-State]:England Locality Name (eg, city) []:London Organization Name (eg, company) [Internet Widgits Pty Ltd]:Solarmora, Inc. Organizational Unit Name (eg, section) []:Creative Publications Common Name (e.g. server FQDN or YOUR name) []:push.solarmora.com Email Address []:admin@solarmora.com
2. Verify that a certificate file was created:
$ myusername@myhost:/tmp$ ls cert*
cert.crt cert.key
3. To get this certificate signed, produce a certificate signing request (CSR) to upload to your signer:
myusername@myhost:/tmp$ sudo openssl x509 -x509toreq -in cert.crt \ -out cert.csr -signkey cert.key Getting request Private Key Generating certificate request
myusername@myhost:/tmp$ ls cert.* cert.crt cert.csr cert.key
4. Ensure that the content of the CSR file looks like this:
Certificate Request: Data: Version: 0 (0x0) Subject: C=GB, ST=England, L=London, O=Solarmora, Inc., OU=Creative Publications, CN=push.solarmora.com/emailAddress=admin@solarmora.com Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:cc:0f:54:26:3d:d9:17:eb:8f:6c:f7:27:5e:77: 64:65:00:db:fe:2a:1f:fa:ea:de:21:7a:c5:5d:87: ... ... Exponent: 65537 (0x10001) Attributes: a0:00 Signature Algorithm: sha256WithRSAEncryption 1d:ea:12:b8:c2:6a:d6:f4:6e:92:2f:b9:12:5e:e3:91:15:a0: 06:b5:81:ce:c5:cf:b7:d2:a7:dd:f2:78:ca:28:8e:21:cd:6d: ... ... -----BEGIN CERTIFICATE REQUEST----- MIIC6zCCAdMCAQAwgaUxCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ8w DQYDVQQHDAZMb25kb24xGDAWBgNVBAoMD0FDTUUgQ29ycCwgSW5jLjEYMBYGA1UE CwwPQ3JlYXRpdmUgQW52aWxzMRswGQYDVQQDDBJwdXNoLmFjbWUtY29ycC5jb20x IjAgBgkqhkiG9w0BCQEWE2FkbWluQGFjbWUtY29ycC5jb20wggEiMA0GCSqGSIb3 ... ... -----END CERTIFICATE REQUEST-----
5. Upload the part of your certificate between the BEGIN
and END
lines (inclusive) to your CA. The exact process will
depend on your CA, but will include the following steps:
- Upload your CSR file to your CA site, or paste the content of your file onto your CA site. Your CA then validates and processes this data.
- Download the signed certificate generated by your CA.
6. The output from the CA should contains multiple files: the signed
certificate itself and the CA's certificate confirming they are eligible to
sign certificates. Concatenate all *.crt
certificate files in the downloaded
bundle into a single bundle file, for example bundle.push.solarmora.com.crt
:
$ cat *.crt > bundle.push.solarmora.com.crt
Configure your proxy server
In this section you configure the NGINX open source web server and reverse proxy server to serve the endpoint and forward all incoming requests to the subscriber server. NGINX is used as an example, but you can use any other HTTP proxy instead.
1. Install NGINX on your server:
$ sudo apt-get update $ sudo apt-get install nginx $ nginx -v $ nginx version: nginx/1.4.6 (Ubuntu)
2. To ensure that the extra server conifguration files you create in the
sites-enabled
directory are processed by NGINX, edit /etc/nginx/nginx.conf
and include the following:
$ include /etc/nginx/conf.d/*.conf; $ include /etc/nginx/sites-enabled/*;
3. Copy your certificate files to a safe location, readable by the
www-data
user, but preferably not readable by any other user (you may need
to adjust the user name if your web server is running as a different
user):
$ sudo mkdir -p /var/openssl/push.solarmora.com $ sudo mv /tmp/cert.key
/var/openssl/push.solarmora.com/push.solarmora.com.key $ sudo mv /tmp/bundle.push.solarmora.com.crt
/var/openssl/push.solarmora.com/bundle.push.solarmora.com.crt
4. Create a new server
configuration. Edit push.solarmora.com
in /etc/nginx/sites-enabled
and use your actual
server's fully qualified domain name as the file name:
server {
listen 443;
server_name push.solarmora.com;
ssl on;
ssl_certificate
/var/openssl/push.solarmora.com/bundle.push.solarmora.com.crt;
ssl_certificate_key
/var/openssl/push.solarmora.com/push.solarmora.com.key;
# it is usually very convenient to have separate files for your
# access and error log to analyze for possible problems
access_log /var/log/nginx/nginx.push.solarmora.com.log;
error_log /var/log/nginx/nginx.push.solarmora.com.log;
location / {
# assuming the subscriber will run on the same machine
# on port 8093
proxy_pass http://localhost:8093;
}
}
5. Restart NGINX to implement the changes:
myusername@myhost:/etc/nginx$ sudo service nginx restart * Restarting nginx nginx ...done.
6. Your server is now configured. To verify the
configuration, try to query your server using curl
:
[myusername@myhost:~]$ curl push.solarmora.com <html> <head><title>502 Bad Gateway</title></head> <body bgcolor="white"> <center><h1>502 Bad Gateway</h1></center> <hr><center>nginx/1.4.6 (Ubuntu)</center> </body> </html>
This is the expected response given that no downstream server has been configured
(localhost:8093
in our config file).
Compile and run examples
To run the examples in this section you need an active Google API Console project. We recommend you create one specifically for testing purposes and keep it separate from your production project. After you create a test project, you need to create a service account. Make a note of the service account email address, and put the associated .p12 file somewhere on your server.
Set up the source code tree
1. Clone the play-work.git
repository:
myusername@myhost:~/code$ git clone https://github.com/google/play-work.git Cloning into 'play-work'... Username for 'https://github.com': username Password for 'https://username@github.com': remote: Counting objects: 110, done. remote: Compressing objects: 100% (60/60), done. remote: Total 110 (delta 24), reused 95 (delta 9), pack-reused 0 Receiving objects: 100% (110/110), 23.88 KiB | 0 bytes/s, done. Resolving deltas: 100% (24/24), done. Checking connectivity... done.2. On Debian-based systems install both Maven and the Google Protocol Buffers compiler:
$ sudo apt-get install maven protobuf-compiler
3. Verify that both Maven and the Google Protocol Buffers compiler are installed correctly:
myusername@myhost:~$ mvn -v Apache Maven 3.0.5 Maven home: /usr/share/maven Java version: 1.7.0_75, vendor: Oracle Corporation Java home: /usr/lib/jvm/java-7-openjdk-amd64/jre Default locale: en_US, platform encoding: UTF-8 OS name: "linux", version: "3.16.0-30-generic", arch: "amd64", family: "unix"
myusername@myhost:~$ protoc --version libprotoc 2.5.0
4. The Maven configuration file pom.xml
assumes that the Protocol Buffers compiler is installed to the /usr/bin/protoc
directory:
myusername@myhost:~$ which protoc /usr/bin/protocIf this is not the case, you can either modify
pom.xml
or symlink protoc
:
$ sudo ln -s which protoc
/usr/bin/protoc
5. Compile the examples. Verify that you can build the code by running mvn clean compile assembly:single
. This should produce a file named
emm-notifications-[version-number]-jar-with-dependencies.jar
, where
[version number]
is the current version of the example, for example 1.0-SNAPSHOT
:
myusername@myhost:~/code/play-work/examples/emm-notifications$ ls target/* target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar6. Verify that you can run the compiled
TestPublisher
code. It is expected that the code will fail:
myusername@myhost:~/code/play-work/examples/emm-notifications$ java -cp \ target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar \ com.google.android.work.emmnotifications.TestPublisher Exception in thread "main" java.lang.IllegalArgumentException: You must specify non-default ServiceAccountEmail in settings.properties at com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument(Preconditions.java:119) at com.google.api.client.util.Preconditions.checkArgument(Preconditions.java:69) at com.google.android.work.emmnotifications.Settings.verifyVariable(Settings.java:129) at com.google.android.work.emmnotifications.Settings.getSettings(Settings.java:103) at com.google.android.work.emmnotifications.TestPublisher.main(TestPublisher.java:39)
7. You must override some values in the settings.properties
file. To do this,
create a copy of the file and modify the properties in the copy as follows:
# This should be your own service account's email address ServiceAccountEmail=368628613713-t4hfexampledn5lhpdcu1qqfgio01626@developer.gserviceaccount.com ServiceAccountP12KeyFile=/opt/secret/secret.p12
# This will be the name of the service account ProjectName=enterprise-cloud-pub-sub SubscriptionName=projects/enterprise-cloud-pub-sub/subscriptions/default TopicName=projects/enterprise-cloud-pub-sub/topics/default
# The push endpoint in your API Console project PushEndpoint=https://push.solarmora.com
8. Run the TestPublisher
code again to make sure it no longer crashes. (You may see a
single error in the log output.)
Run the publisher test code
You need to run the sample code for publishing notifications so that your subscriber has some messages to read.
In the following example, the code looks for the
topic specified in my_settings.properties
but doesn't find it, and therefore it creates the topic.
It then publishes a message to the topic. This example provides a valuable
testing tool that allows you to emulate messages sent by the Google Play EMM API.
myusername@myhost:~/code/play-work/examples/emm-notifications$ DEVELOPER_CONSOLE_SETTINGS=./my_settings.properties java -cp \ target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar com.google.android.work.emmnotifications.TestPublisher Feb 27, 2015 1:39:59 PM com.google.android.work.emmnotifications.RetryHttpInitializerWrapper$1 handleResponse INFO: RetryHandler: { "error": { "code": 404, "message": "Resource not found (resource=default).", "errors": [ { "message": "Resource not found (resource=default).", "domain": "global", "reason": "notFound" } ], "status": "NOT_FOUND" } } Feb 27, 2015 1:39:59 PM com.google.android.work.emmnotifications.TestPublisher main INFO: Topic projects/enterprise-cloud-pub-sub/topics/default doesn't exists, creating it Feb 27, 2015 1:40:02 PM com.google.android.work.emmnotifications.TestPublisher main INFO: Topic projects/enterprise-cloud-pub-sub/topics/default created Feb 27, 2015 1:40:02 PM com.google.android.work.emmnotifications.TestPublisher main INFO: Publishing a request: {messages=[{data=CjEKFQoIMTIzMjEzMjESCXJpZ2h0IG5vdxIWY29tLmdvb2dsZS5hbmRyb2lkLmdtcxgA}]}
Run the subscriber test code
The subscriber test code confirms that you can receive the messages published by
the TestPublisher
code.
1. Ensure that your code is up to date and compiled, and then run the subscriber test code:
myusername@myhost:~/code/play-work/examples/emm-notifications$ DEVELOPER_CONSOLE_SETTINGS=./my_settings.propertiesThe subscriber is now running and ready to accept incoming messages.
java -cp target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar
com.google.android.work.emmnotifications.PushSubscriber Feb 27, 2015 1:46:37 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Will be using topic name: projects/enterprise-cloud-pub-sub/topics/default, subscription name:
projects/enterprise-cloud-pub-sub/subscriptions/default Feb 27, 2015 1:46:38 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Trying to get subscription named projects/enterprise-cloud-pub-sub/subscriptions/default Feb 27, 2015 1:46:38 PM com.google.android.work.emmnotifications.RetryHttpInitializerWrapper$1 handleResponse INFO: RetryHandler: { "error": { "code": 404, "message": "Resource not found (resource=default).", "errors": [ { "message": "Resource not found (resource=default).", "domain": "global", "reason": "notFound" } ], "status": "NOT_FOUND" } } Feb 27, 2015 1:46:38 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Subscription doesn't exist, will try to create projects/enterprise-cloud-pub-sub/subscriptions/default Feb 27, 2015 1:46:43 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Created: { "ackDeadlineSeconds" : 600, "name" : "projects/enterprise-cloud-pub-sub/subscriptions/default", "pushConfig" : { "pushEndpoint" : "https://push.solarmora.com" }, "topic" : "projects/enterprise-cloud-pub-sub/topics/default" }
2. Run the publisher again, and new messages are added to the log:
Feb 27, 2015 1:47:24 PM com.google.android.work.emmnotifications.PushSubscriber$1 handle INFO: Raw request: {"message":{"data":"CjEKFQoIMTIzMjEzMjESCXJpZ2h0IG5vdxIWY29tLmdvb2dsZS5hbmRyb2lkLmdtcxgA",A message has been properly received and processed.
"attributes":{},"message_id":"71571141246"},"subscription":"/subscriptions/enterprise-cloud-pub-sub/default"} Feb 27, 2015 1:47:24 PM com.google.android.work.emmnotifications.PushSubscriber$1 handle INFO: Pubsub message received: { "attributes" : { }, "data" : "CjEKFQoIMTIzMjEzMjESCXJpZ2h0IG5vdxIWY29tLmdvb2dsZS5hbmRyb2lkLmdtcxgA", "message_id" : "71571141246" } Feb 27, 2015 1:47:24 PM com.google.android.work.emmnotifications.PushSubscriber$1 handle INFO: Message received: product_approval_event { common_event_information { enterprise_id: "12321321" event_notification_sent_timestamp: "right now" } product_id: "com.google.android.gms" approved: false }