/** * Copyright (c) 2011, SOCIETIES Consortium (WATERFORD INSTITUTE OF TECHNOLOGY (TSSG), HERIOT-WATT UNIVERSITY (HWU), SOLUTA.NET * (SN), GERMAN AEROSPACE CENTRE (Deutsches Zentrum fuer Luft- und Raumfahrt e.V.) (DLR), Zavod za varnostne tehnologije * informacijske družbe in elektronsko poslovanje (SETCCE), INSTITUTE OF COMMUNICATION AND COMPUTER SYSTEMS (ICCS), LAKE * COMMUNICATIONS (LAKE), INTEL PERFORMANCE LEARNING SOLUTIONS LTD (INTEL), PORTUGAL TELECOM INOVAÇÃO, SA (PTIN), IBM Corp., * INSTITUT TELECOM (ITSUD), AMITEC DIACHYTI EFYIA PLIROFORIKI KAI EPIKINONIES ETERIA PERIORISMENIS EFTHINIS (AMITEC), TELECOM * ITALIA S.p.a.(TI), TRIALOG (TRIALOG), Stiftelsen SINTEF (SINTEF), NEC EUROPE LTD (NEC)) * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.societies.android.platform.css.friends; import java.util.HashSet; import java.util.List; import java.util.Set; import org.societies.android.api.comms.IMethodCallback; import org.societies.android.api.comms.xmpp.ICommCallback; import org.societies.android.api.comms.xmpp.Stanza; import org.societies.android.api.comms.xmpp.VCardParcel; import org.societies.android.api.comms.xmpp.XMPPError; import org.societies.android.api.comms.xmpp.XMPPInfo; import org.societies.android.api.events.IAndroidSocietiesEvents; import org.societies.android.api.utilities.ServiceMethodTranslator; import org.societies.android.platform.androidutils.AndroidNotifier; import org.societies.android.platform.comms.helper.ClientCommunicationMgr; import org.societies.api.schema.css.directory.CssAdvertisementRecord; import org.societies.api.schema.css.directory.CssFriendEvent; import android.app.Notification; import android.app.Service; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; /** * Describe your class here... * * @author aleckey * */ public class EventService extends Service { private static final String LOG_TAG = EventService.class.getName(); private static final String SERVICE_ACTION = "org.societies.android.platform.events.ServicePlatformEventsRemote"; private static final String CLIENT_NAME = "org.societies.android.platform.css.friends.EventService"; private static final String EXTRA_CSS_ADVERT = "org.societies.api.schema.css.directory.CssAdvertisementRecord"; private static final String EXTRA_CSS_VCARD = "org.societies.android.api.comms.xmpp.VCardParcel"; private static final String ALL_CSS_FRIEND_INTENTS = "org.societies.android.css.friends"; //TRACKING CONNECTION TO EVENTS MANAGER private boolean boundToEventMgrService = false; private Messenger eventMgrService = null; private BroadcastReceiver receiver; private Looper mServiceLooper; private ServiceHandler mServiceHandler; private ClientCommunicationMgr ccm; private final Set<String> processedIncomingRequests = new HashSet<String>(); // Handler that receives messages from the thread private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { Log.d(this.getClass().getName(), "Message received in Friends Service thread"); if (!boundToEventMgrService) { setupBroadcastReceiver(); bindToEventsManagerService(); } // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job //stopSelf(msg.arg1); } } //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>FRIENDS SERVICE LIFECYCLE METHODS>>>>>>>>>>>>>>>>>>>>>>>>>>>> @Override public IBinder onBind(Intent intent) { return null; //NO-ONE ALLOWED TO BIND TO THIS SERVICE } @Override public void onCreate() { Log.d(this.getClass().getName(), "Friends Service creating..."); this.ccm = new ClientCommunicationMgr(this, true); this.ccm.bindCommsService(new IMethodCallback() { @Override public void returnException(String result) { Log.d(LOG_TAG, "Exception binding to service: " + result); } @Override public void returnAction(String result) { Log.d(LOG_TAG, "return Action.flag: " + result); } @Override public void returnAction(boolean resultFlag) { Log.d(LOG_TAG, "return Action.flag: " + resultFlag); } }); // START BACKGROUND THREAD FOR SERVICE HandlerThread thread = new HandlerThread("FriendServiceStartArguments", android.os.Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // Get the HandlerThread's Looper and use it for our Handler mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // For each start request, send a message to start a job and deliver the //start ID so we know which request we're stopping when we finish the job Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; mServiceHandler.sendMessage(msg); //If we get killed, after returning from here, restart return START_STICKY; } @Override public void onDestroy() { Log.d(LOG_TAG, "Friends service terminating"); boundToEventMgrService = false; this.unregisterReceiver(receiver); this.unbindService(serviceConnection); } //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>BIND TO EXTERNAL "EVENT MANAGER">>>>>>>>>>>>>>>>>>>>>>>>>>>> /** Bind to the Events Manager Service */ private void bindToEventsManagerService() { Log.d(LOG_TAG, "Binding to Events Manager Service..."); Intent serviceIntent = new Intent(SERVICE_ACTION); bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE); } private ServiceConnection serviceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { boundToEventMgrService = true; eventMgrService = new Messenger(service); Log.d(this.getClass().getName(), "Connected to the Societies Event Mgr Service"); //BOUND TO SERVICE - SUBSCRIBE TO RELEVANT EVENTS InvokeRemoteMethod invoke = new InvokeRemoteMethod(CLIENT_NAME); invoke.execute(); } public void onServiceDisconnected(ComponentName name) { boundToEventMgrService = false; } }; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>REGISTER FOR EVENTS>>>>>>>>>>>>>>>>>>>>>>>>>>>> /**Create a broadcast receiver */ private void setupBroadcastReceiver() { Log.d(LOG_TAG, "Setting up broadcast receiver..."); receiver = new MainReceiver(); this.registerReceiver(receiver, createIntentFilter()); Log.d(LOG_TAG, "Registered broadcast receiver"); } /**Broadcast receiver to receive intent return values from EventManager service*/ private class MainReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.d(LOG_TAG, "Received action: " + intent.getAction()); //EVENT MANAGER INTENTS if (intent.getAction().equals(IAndroidSocietiesEvents.SUBSCRIBE_TO_EVENT)) { Log.d(LOG_TAG, "Subscribed to event: " + intent.getBooleanExtra(IAndroidSocietiesEvents.INTENT_RETURN_VALUE_KEY, false)); } else if (intent.getAction().equals(IAndroidSocietiesEvents.SUBSCRIBE_TO_EVENTS)) { Log.d(LOG_TAG, "Subscribed to multiple events: " + intent.getBooleanExtra(IAndroidSocietiesEvents.INTENT_RETURN_VALUE_KEY, false)); } else if (intent.getAction().equals(IAndroidSocietiesEvents.UNSUBSCRIBE_FROM_EVENTS)) { Log.d(LOG_TAG, "Un-subscribed to events: " + intent.getBooleanExtra(IAndroidSocietiesEvents.INTENT_RETURN_VALUE_KEY, false)); } //PUBSUB EVENTS - payload is CssFriendEvent else if (intent.getAction().equals(IAndroidSocietiesEvents.CSS_FRIEND_REQUEST_RECEIVED_INTENT)) { Log.d(LOG_TAG, "Frient Request received event"); CssFriendEvent eventPayload = intent.getParcelableExtra(IAndroidSocietiesEvents.GENERIC_INTENT_PAYLOAD_KEY); //BUGFIX TO STOP MULTIPLE NOTIFICATIONS FOR SAME REQUEST (UNKNOWN ROOT CAUSE) String receivedID = eventPayload.getCssAdvert().getId(); synchronized(processedIncomingRequests) { if (processedIncomingRequests.contains(receivedID)) { Log.d(LOG_TAG, "Ignoring duplicate request from: " + receivedID); return; } processedIncomingRequests.add(receivedID); } //CONTINUE - GET VCARD FOR THIS REQUEST ICommCallback callback = new VCardCallback(eventPayload); ccm.getVCard(eventPayload.getCssAdvert().getId(), callback); } else if (intent.getAction().equals(IAndroidSocietiesEvents.CSS_FRIEND_REQUEST_ACCEPTED_INTENT)) { Log.d(LOG_TAG, "Frient Request accepted event"); CssFriendEvent eventPayload = intent.getParcelableExtra(IAndroidSocietiesEvents.GENERIC_INTENT_PAYLOAD_KEY); String description = eventPayload.getCssAdvert().getName() + " accepted your friend request"; addNotificationAccept(description, "Friend Request Accepted", eventPayload.getCssAdvert()); } } } /**Create a suitable intent filter * @return IntentFilter */ private IntentFilter createIntentFilter() { //register broadcast receiver to receive SocietiesEvents return values IntentFilter intentFilter = new IntentFilter(); //EVENT MANAGER INTENTS intentFilter.addAction(IAndroidSocietiesEvents.SUBSCRIBE_TO_EVENT); intentFilter.addAction(IAndroidSocietiesEvents.SUBSCRIBE_TO_EVENTS); intentFilter.addAction(IAndroidSocietiesEvents.UNSUBSCRIBE_FROM_EVENTS); //PUBSUB INTENTS intentFilter.addAction(IAndroidSocietiesEvents.CSS_FRIEND_REQUEST_RECEIVED_INTENT); intentFilter.addAction(IAndroidSocietiesEvents.CSS_FRIEND_REQUEST_ACCEPTED_INTENT); return intentFilter; } //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>SUBSCRIBE TO PUBSUB EVENTS>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /** Async task to invoke remote service method */ private class InvokeRemoteMethod extends AsyncTask<Void, Void, Void> { private final String LOCAL_LOG_TAG = InvokeRemoteMethod.class.getName(); private String client; public InvokeRemoteMethod(String client) { this.client = client; } protected Void doInBackground(Void... args) { //METHOD: subscribeToEvents(String client, String intentFilter) - ARRAY POSITION: 1 String targetMethod = IAndroidSocietiesEvents.methodsArray[1]; Message outMessage = Message.obtain(null, ServiceMethodTranslator.getMethodIndex(IAndroidSocietiesEvents.methodsArray, targetMethod), 0, 0); Bundle outBundle = new Bundle(); //PARAMETERS outBundle.putString(ServiceMethodTranslator.getMethodParameterName(targetMethod, 0), this.client); outBundle.putString(ServiceMethodTranslator.getMethodParameterName(targetMethod, 1), ALL_CSS_FRIEND_INTENTS); Log.d(LOCAL_LOG_TAG, "Client Package Name: " + this.client); outMessage.setData(outBundle); Log.d(LOCAL_LOG_TAG, "Sending event registration"); try { eventMgrService.send(outMessage); } catch (RemoteException e) { e.printStackTrace(); } return null; } } private void addNotificationAccept(String description, String eventType, CssAdvertisementRecord advert) { //CREATE ANDROID NOTIFICATION int notifierflags[] = new int[] {Notification.DEFAULT_SOUND, Notification.DEFAULT_VIBRATE, Notification.FLAG_AUTO_CANCEL}; AndroidNotifier notifier = new AndroidNotifier(EventService.this.getApplicationContext(), Notification.DEFAULT_SOUND, notifierflags); //CREATE INTENT FOR LAUNCHING ACTIVITY //Intent intent = new Intent(); //PackageManager manager = getPackageManager(); //intent = manager.getLaunchIntentForPackage("org.societies.android.platform.gui"); //intent.addCategory(Intent.CATEGORY_LAUNCHER); ///intent.putExtra(EXTRA_CSS_ADVERT, (Parcelable)advert); //intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); notifier.notifyMessage(description, eventType, AcceptFriendActivity.class); } /** * Callback used with Android Comms for CSSDirectory * */ private class VCardCallback implements ICommCallback { private CssFriendEvent eventPayload; public VCardCallback(CssFriendEvent eventPayload) { this.eventPayload = eventPayload; } public List<String> getXMLNamespaces() { return null;} public List<String> getJavaPackages() { return null;} public void receiveError(Stanza arg0, XMPPError arg1) { } public void receiveInfo(Stanza arg0, String arg1, XMPPInfo arg2) { } public void receiveItems(Stanza arg0, String arg1, List<String> arg2) { } public void receiveMessage(Stanza arg0, Object arg1) { } public void receiveResult(Stanza arg0, Object retValue) { Log.d(VCardCallback.class.getName(), "VCardCallback Callback receiveResult"); VCardParcel vCard = (VCardParcel)retValue; //CREATE ANDROID NOTIFICATION int notifierflags [] = new int [1]; notifierflags[0] = Notification.FLAG_AUTO_CANCEL; AndroidNotifier notifier = new AndroidNotifier(EventService.this.getApplicationContext(), Notification.DEFAULT_SOUND, notifierflags); //CREATE INTENT FOR LAUNCHING ACTIVITY Intent intent = new Intent(EventService.this.getApplicationContext(), AcceptFriendActivity.class); intent.putExtra(EXTRA_CSS_ADVERT, (Parcelable)eventPayload.getCssAdvert()); intent.putExtra(EXTRA_CSS_VCARD, (Parcelable)vCard); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); String description = eventPayload.getCssAdvert().getName() + " sent a friend request"; notifier.notifyMessage(description, "Friend Request", AcceptFriendActivity.class, intent, "SOCIETIES"); } } }