/** * Copyright (c) 2011-2014, OpenIoT * * This file is part of OpenIoT. * * OpenIoT is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * OpenIoT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with OpenIoT. If not, see <http://www.gnu.org/licenses/>. * * Contact: OpenIoT mailto: info@openiot.eu */ package org.openiot.cupus.mobile.entity.mobilebroker; import android.app.Activity; import android.app.IntentService; import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.support.v4.app.NotificationCompat; import android.support.v4.content.WakefulBroadcastReceiver; import android.util.Log; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.gcm.GoogleCloudMessaging; import org.openiot.cupus.message.Message; import org.openiot.cupus.message.external.MobileBrokerRegisterGCMMessage; import org.openiot.cupus.message.external.NotifyMessage; import org.openiot.cupus.message.external.NotifySubscriptionMessage; import org.openiot.cupus.mobile.application.MobileBrokerActivity; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; /** * @author Marko */ public class GCMMobileBroker extends MobileBrokerOutgoingTCP { private static GCMMobileBroker gcmMobileBroker; public static final String PROPERTY_REG_ID = "";// "registration_id"; private static final String PROPERTY_APP_VERSION = "1"; private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; /** * Substitute you own sender ID here. This is the project number you got * from the API Console, as described in "Getting Started." */ private String SENDER_ID = "362337850870"; private Context context; private Activity activity; private String regID; private GoogleCloudMessaging gcm; public GCMMobileBroker(String myName, String myBrokerIP, int myBrokerPort, Context context, Activity activity) { super(myName, myBrokerIP, myBrokerPort, context); this.context = context; this.activity = activity; gcmMobileBroker = this; } /** * Constructor - subscriber can be created via configuration file or * directly */ public GCMMobileBroker(File configFile, Context context, Activity activity) { super(configFile, context); this.context = context; this.activity = activity; gcmMobileBroker = this; } @Override protected void startIncomingConnection() { String registrationID = null; // Check device for Play Services APK. If check succeeds, proceed with // GCM registration. if (checkPlayServices()) { gcm = GoogleCloudMessaging.getInstance(context); registrationID = getRegistrationId(context); if (registrationID.isEmpty()) { try { if (gcm == null) { gcm = GoogleCloudMessaging.getInstance(context); } registrationID = gcm.register(SENDER_ID); System.out.println("Device registered, registration ID=" + registrationID); } catch (IOException e) { } this.regID = registrationID; } } else { Log.i("CUPUS", "No valid Google Play Services APK found."); } //send the register message Message connectMessage = new MobileBrokerRegisterGCMMessage(myName, this.getId(), this.regID); this.sendMessageInBackGround(connectMessage); this.connected = true; log.writeToLog("Connected to GCM Broker " + registrationID); IntentFilter filter = new IntentFilter(); filter.addAction("com.google.android.c2dm.intent.RECEIVE"); filter.addCategory("org.openiot.cupus.mobile.application"); GcmBroadcastReceiver receiver = new GcmBroadcastReceiver(); if (activity instanceof MobileBrokerActivity) { activity.registerReceiver(receiver, filter); ((MobileBrokerActivity) activity).setBroadcastReceiver(receiver); ((MobileBrokerActivity) activity).setGcmReceiver(true); } } @Override protected void terminateIncomingConnectionInBackground() { try { gcm.unregister(); } catch (IOException e) { e.printStackTrace(); } } /** * Gets the current registration ID for application on GCM service. * <p/> * If result is empty, the app needs to register. * * @return registration ID, or empty string if there is no existing * registration ID. */ private String getRegistrationId(Context context) { final SharedPreferences prefs = getGCMPreferences(context); String registrationId = prefs.getString(PROPERTY_REG_ID, ""); if (registrationId.isEmpty()) { Log.i("CUPUS", "Registration not found."); return ""; } // Check if app was updated; if so, it must clear the registration ID // since the existing regID is not guaranteed to work with the new // app version. int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE); int currentVersion = this.getAppVersion(context); if (registeredVersion != currentVersion) { Log.i("CUPUS", "App version changed."); return ""; } return registrationId; } /** * @return Application's {@code SharedPreferences}. */ private SharedPreferences getGCMPreferences(Context context) { // This sample app persists the registration ID in shared preferences, but // how you store the regID in your app is up to you. return activity.getSharedPreferences(activity.getClass().getSimpleName(), Context.MODE_PRIVATE); } /** * @return Application's version code from the {@code PackageManager}. */ private static int getAppVersion(Context context) { try { PackageInfo packageInfo = context.getPackageManager() .getPackageInfo(context.getPackageName(), 0); return packageInfo.versionCode; } catch (NameNotFoundException e) { // should never happen throw new RuntimeException("Could not get package name: " + e); } } private boolean checkPlayServices() { int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(activity); if (resultCode != ConnectionResult.SUCCESS) { if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { GooglePlayServicesUtil.getErrorDialog(resultCode, activity, PLAY_SERVICES_RESOLUTION_REQUEST).show(); } else { Log.i("CUPUS", "This device is not supported."); activity.finish(); } return false; } return true; } public class GcmBroadcastReceiver extends WakefulBroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // Explicitly specify that GcmIntentService will handle the intent. ComponentName comp = new ComponentName(context.getPackageName(), GcmIntentService.class.getName()); // Start the service, keeping the device awake while it is launching. startWakefulService(context, (intent.setComponent(comp))); setResultCode(Activity.RESULT_OK); } } public static class GcmIntentService extends IntentService { public static final int NOTIFICATION_ID = 1; private NotificationManager mNotificationManager; NotificationCompat.Builder builder; GCMMobileBroker broker; public GcmIntentService() { super("GcmIntentService"); this.broker = GCMMobileBroker.gcmMobileBroker; } @Override protected void onHandleIntent(Intent intent) { Bundle extras = intent.getExtras(); GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this); // The getMessageType() intent parameter must be the intent you received // in your BroadcastReceiver. String messageType = gcm.getMessageType(intent); if (!extras.isEmpty()) { // has effect of unparcelling Bundle /* * Filter messages based on message type. Since it is likely that GCM * will be extended in the future with new message types, just ignore * any message types you're not interested in, or that you don't * recognize. */ if (GoogleCloudMessaging. MESSAGE_TYPE_SEND_ERROR.equals(messageType)) { //TODO za error msg // sendNotification("Send error: " + extras.toString()); } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) { //TODO za deleted msg // sendNotification("Deleted messages on server: " + extras.toString()); // If it's a regular GCM message, do some work. } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) { String message = extras.getString("message", ""); System.out.println("Received msg: " + message); broker.log.writeToLog("Received msg: " + message); if (message.isEmpty()) { System.out.println("Received empty msg"); broker.log.writeToLog("Received empty msg"); return; } Object objIn = null; byte[] byteMsg = android.util.Base64.decode(message, android.util.Base64.DEFAULT); ByteArrayInputStream bis = new ByteArrayInputStream(byteMsg); ObjectInput in = null; try { try { in = new ObjectInputStream(bis); objIn = in.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } finally { try { bis.close(); } catch (IOException ex) { // ignore close exception } try { if (in != null) { in.close(); } } catch (IOException ex) { // ignore close exception } } if (objIn instanceof NotifyMessage) { NotifyMessage msg = (NotifyMessage) objIn; broker.notify(msg.getPublication(), msg.isUnpublish()); } else if (objIn instanceof NotifySubscriptionMessage) { NotifySubscriptionMessage msg = (NotifySubscriptionMessage) objIn; broker.announcement(msg.getSubscription(), msg.isRevoke()); } else { broker.log.writeToLog("Unkown request/response received from broker (type = " + objIn.getClass().getName() + "). Ignoring..."); } } } // Release the wake lock provided by the WakefulBroadcastReceiver. GcmBroadcastReceiver.completeWakefulIntent(intent); } } }