/** * 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.comms; import java.io.IOException; import java.io.StringReader; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.jivesoftware.smack.AccountManager; import org.jivesoftware.smack.Connection; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.filter.PacketIDFilter; import org.jivesoftware.smack.filter.PacketTypeFilter; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smackx.packet.DiscoverItems; import org.jivesoftware.smackx.packet.VCard; import org.societies.android.api.comms.XMPPAgent; import org.societies.android.api.comms.xmpp.VCardParcel; import org.societies.android.api.comms.xmpp.XMPPNode; import org.societies.android.platform.androidutils.AndroidNotifier; import org.societies.android.platform.comms.state.IConnectionState; import org.societies.android.platform.comms.state.NoXMPPConnectionAvailableException; import org.societies.android.platform.comms.state.XMPPConnectionManager; import org.societies.android.platform.comms.state.XMPPConnectionProperties; import org.societies.android.platform.comms.state.IConnectionState.ConnectionState; import org.societies.utilities.DBC.Dbc; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import android.app.Notification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Parcelable; import android.util.Log; public class AndroidCommsBase implements XMPPAgent { private static final String LOG_TAG = AndroidCommsBase.class.getName(); private static final long PUBSUB_EVENT_CALLBACK_ID = -9999999999999L; private static final long INVALID_LONG_INTENT_VALUE = -9999999999998L; private static final String PUBSUB_NAMESPACE_KEY = "http://jabber.org/protocol"; private static final boolean DEBUG_LOGGING = false; public static final String NOTIFICATION_TITLE = "Societies Communications Problem"; public static final String COMMS_RESTORED_CONNECTIVITY = "Re-connected"; public static final String COMMS_NO_CONNECTIVITY = "NotConnected"; private static final String COMMS_CANNOT_REGISTER = "RegistrationError"; private static final String COMMS_CANNOT_UNREGISTER = "UnRegistrationError"; private static final String COMMS_CANNOT_SEND_MESSAGE = "SendMessageError"; private static final String COMMS_CANNOT_SEND_IQ = "SendIQError"; private static final String COMMS_CANNOT_CREATE_ID = "IdCreationError"; private static final String COMMS_CANNOT_LOGIN = "LoginError"; private static final String COMMS_CANNOT_LOGOUT = "LogoutError"; // private String username, password, resource; private String resource; private ProviderElementNamespaceRegistrar providerRegistrar = new ProviderElementNamespaceRegistrar(); private RawXmlProvider rawXmlProvider = new RawXmlProvider(); private String domainAuthorityNode; int port; boolean debug; boolean restrictBroadcast; boolean pubsubRegistered; PacketListener pubsubListener; Context serviceContext; BroadcastReceiver androidCommsReceiver; BroadcastReceiver xmppConnectionReceiver; IConnectionState xmppConnectMgr; private boolean lostConnection; public AndroidCommsBase(Context serviceContext, boolean restrictBroadcast) { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Service Base Object created"); }; this.restrictBroadcast = restrictBroadcast; if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Restrict broadcasted intents: " + this.restrictBroadcast); }; this.serviceContext = serviceContext; this.pubsubRegistered = false; this.pubsubListener = null; this.lostConnection = false; //Use the XMPPConnectionManager to access the aSmack XMPP connection this.xmppConnectMgr = new XMPPConnectionManager(); //Android Profiling // Debug.startMethodTracing(this.getClass().getSimpleName()); } /** * Carry out any actions required before shutting down the service */ public void serviceCleanup() { if (null != androidCommsReceiver) { this.teardownBroadcastReceiver(androidCommsReceiver); androidCommsReceiver = null; } if (null != xmppConnectionReceiver) { this.teardownBroadcastReceiver(xmppConnectionReceiver); xmppConnectionReceiver = null; } } public boolean register(String client, String[] elementNames, String[] namespaces, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("Message Beans must be specified", null != elementNames && elementNames.length > 0); Dbc.require("Namespaces must be specified", null != namespaces && namespaces.length > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "register for client: " + client); // for (String element : elementNames) { // Log.d(LOG_TAG, "register element name: " + element); // } // for (String namespace : namespaces) { // Log.d(LOG_TAG, "register namespace: " + namespace); // } }; for(int i=0; i<elementNames.length; i++) { for(int j=0; j<namespaces.length; j++) { providerRegistrar.register(new ProviderElementNamespaceRegistrar.ElementNamespaceTuple(elementNames[i], namespaces[j])); ProviderManager.getInstance().addIQProvider(elementNames[i], namespaces[j], rawXmlProvider); ProviderManager.getInstance().addExtensionProvider(elementNames[i], namespaces[j], rawXmlProvider); } } //Send intent Intent intent = new Intent(); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); try { //only need to register Pubsub listener once, otherwise multiple events instances //are generated for a single event if (!this.pubsubRegistered && isNameSpacePubsub(namespaces)) { // connect(); this.pubsubListener = new RegisterPacketListener(client, remoteCallId); this.xmppConnectMgr.getValidConnection().addPacketListener(this.pubsubListener, new AndFilter(new PacketTypeFilter(Message.class), new NamespaceFilter(namespaces))); this.pubsubRegistered = true; if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Pubsub event listener registered"); }; } intent.setAction(XMPPAgent.REGISTER_RESULT); intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, true); } catch (NoXMPPConnectionAvailableException e) { Log.e(LOG_TAG, e.getMessage(), e); intent.setAction(XMPPAgent.REGISTER_EXCEPTION); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); createNotification("Error registering namespaces: " + e.getMessage(), COMMS_CANNOT_REGISTER, NOTIFICATION_TITLE); } finally { AndroidCommsBase.this.serviceContext.sendBroadcast(intent); } return false; } public boolean unregister(String client, String[] elementNames, String[] namespaces, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("Message Beans must be specified", null != elementNames && elementNames.length > 0); Dbc.require("Namespaces must be specified", null != namespaces && namespaces.length > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "unregister for client: " + client); // for (String element : elementNames) { // Log.d(LOG_TAG, "unregister element name: " + element); // } // for (String namespace : namespaces) { // Log.d(LOG_TAG, "unregister namespace: " + namespace); // } }; //Send intent Intent intent = new Intent(); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(XMPPAgent.INTENT_RETURN_CALL_ID_KEY, remoteCallId); try { //remove Pubsub listener if (this.pubsubRegistered && isNameSpacePubsub(namespaces)) { this.xmppConnectMgr.getValidConnection().removePacketListener(this.pubsubListener); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Pubsub event listener unregistered"); }; this.pubsubRegistered = false; } for(int i=0; i<elementNames.length; i++) { for(int j=0; j<namespaces.length; j++) { ProviderElementNamespaceRegistrar.ElementNamespaceTuple tuple = new ProviderElementNamespaceRegistrar.ElementNamespaceTuple(elementNames[i], namespaces[j]); providerRegistrar.unregister(tuple); if(!providerRegistrar.isRegistered(tuple)) { removeProviders(tuple); } } } intent.setAction(XMPPAgent.UNREGISTER_RESULT); intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, true); } catch (NoXMPPConnectionAvailableException e) { Log.e(LOG_TAG, e.getMessage(), e); intent.setAction(XMPPAgent.UNREGISTER_EXCEPTION); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); createNotification("Error un-registering namespaces: " + e.getMessage(), COMMS_CANNOT_UNREGISTER, NOTIFICATION_TITLE); } catch (Exception e) { Log.e(LOG_TAG, e.getMessage(), e); intent.setAction(XMPPAgent.UNREGISTER_EXCEPTION); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); createNotification("Error un-registering namespaces: " + e.getMessage(), COMMS_CANNOT_UNREGISTER, NOTIFICATION_TITLE); } finally { AndroidCommsBase.this.serviceContext.sendBroadcast(intent); //Android Profiling // Debug.stopMethodTracing(); } return false; } public boolean UnRegisterCommManager(String client, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "UnRegisterCommManager for client: " + client); }; boolean retValue = false; //Send intent Intent intent = new Intent(); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(XMPPAgent.INTENT_RETURN_CALL_ID_KEY, remoteCallId); try { retValue = UnRegisterCommManagerInternal(); intent.setAction(XMPPAgent.UN_REGISTER_COMM_MANAGER_RESULT); intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, retValue); } catch (Exception e) { Log.e(LOG_TAG, e.getMessage(), e); intent.setAction(XMPPAgent.UN_REGISTER_COMM_MANAGER_EXCEPTION); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); createNotification("Error unregistering all namespaces: " + e.getMessage(), COMMS_CANNOT_UNREGISTER, NOTIFICATION_TITLE); } finally { this.serviceContext.sendBroadcast(intent); } return false; } public boolean sendMessage(String client, String messageXml, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("XML must be specified", null != messageXml && messageXml.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "sendMessage for client: " + client); }; //Send intent Intent intent = new Intent(); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(XMPPAgent.INTENT_RETURN_CALL_ID_KEY, remoteCallId); Packet formattedMessage = createPacketFromXml(messageXml); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "sendMessage xml message size: " + formattedMessage.toXML().length()); Log.d(LOG_TAG, "sendMessage xml message:" + createPacketFromXml(messageXml).toXML()); }; try { this.xmppConnectMgr.getValidConnection().sendPacket(formattedMessage); intent.setAction(XMPPAgent.SEND_MESSAGE_RESULT); intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, true); } catch (NoXMPPConnectionAvailableException e) { Log.e(LOG_TAG, e.getMessage(), e); intent.setAction(XMPPAgent.SEND_MESSAGE_EXCEPTION); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); createNotification("Error sending message due to lost connectivity", COMMS_NO_CONNECTIVITY, NOTIFICATION_TITLE); this.lostConnection = true; } catch (Exception e) { Log.e(LOG_TAG, e.getMessage(), e); intent.setAction(XMPPAgent.SEND_MESSAGE_EXCEPTION); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); createNotification("Error sending message: " + e.getMessage(), COMMS_CANNOT_SEND_MESSAGE, NOTIFICATION_TITLE); } finally { this.serviceContext.sendBroadcast(intent); } return false; } public boolean sendIQ(String client, String xml, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("XML must be specified", null != xml && xml.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "sendIQ xml: " + xml+ " for client: " + client); }; try { // connect(); String id = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(xml))).getDocumentElement().getAttribute("id"); if(id.equals("")) { throw new NullPointerException("IQ XML has no ID attribute!"); } this.xmppConnectMgr.getValidConnection().addPacketListener(new SendIQPacketListener(client, remoteCallId), new AndFilter(new PacketTypeFilter(IQ.class),new PacketIDFilter(id))); this.xmppConnectMgr.getValidConnection().sendPacket(createPacketFromXml(xml)); } catch (NoXMPPConnectionAvailableException e) { Log.e(LOG_TAG, e.getMessage(), e); //Send intent Intent intent = new Intent(SEND_IQ_EXCEPTION); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); createNotification("Error invoking remote method", COMMS_NO_CONNECTIVITY, NOTIFICATION_TITLE); this.lostConnection = true; } catch (Exception e) { Log.e(LOG_TAG, e.getMessage(), e); //Send intent Intent intent = new Intent(SEND_IQ_EXCEPTION); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); createNotification("Error invoking remote method due to lost connectivity: " + e.getMessage(), COMMS_CANNOT_SEND_IQ, NOTIFICATION_TITLE); } return false; } public String getIdentity(String client, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "getIdentity for client: " + client); }; String retValue = null; //Send intent Intent intent = new Intent(GET_IDENTITY); if (this.restrictBroadcast) { intent.setPackage(client); } try { // connect(); retValue = this.xmppConnectMgr.getValidConnection().getUser(); // disconnect(); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "getIdentity identity: " + retValue); }; } catch (NoXMPPConnectionAvailableException e) { Log.e(LOG_TAG, e.getMessage(), e); } finally { intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, retValue); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); this.serviceContext.sendBroadcast(intent); } return null; } public String getDomainAuthorityNode(String client, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "getDomainAuthorityNode for client: " + client); }; //Send intent Intent intent = new Intent(GET_DOMAIN_AUTHORITY_NODE); intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, this.domainAuthorityNode); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); if (this.restrictBroadcast) { intent.setPackage(client); } this.serviceContext.sendBroadcast(intent); return null; } public String getItems(String client, String entity, String node, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("Entity must be specified", null != entity && entity.length() > 0); Dbc.require("Node must be specified", null != node && node.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "getItems entity: " + entity + " for client: " + client); }; String retValue = null; try { // connect(); DiscoverItems discoItems = new DiscoverItems(); discoItems.setTo(entity); discoItems.setNode(node); this.xmppConnectMgr.getValidConnection().addPacketListener(new GetItemsPacketListener(client, remoteCallId), new AndFilter(new PacketTypeFilter(IQ.class),new PacketIDFilter(discoItems.getPacketID()))); this.xmppConnectMgr.getValidConnection().sendPacket(discoItems); retValue = discoItems.getPacketID(); } catch (NoXMPPConnectionAvailableException e) { Log.e(LOG_TAG, e.getMessage(), e); //Send intent Intent intent = new Intent(GET_ITEMS_EXCEPTION); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, ""); intent.putExtra(INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); } return null; } public boolean isConnected(String client, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "isConnected for client: " + client); }; boolean retValue = false; Intent intent = new Intent(IS_CONNECTED); if (this.restrictBroadcast) { intent.setPackage(client); } if (this.xmppConnectMgr.isConnected()) { retValue = true; } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, retValue); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); this.serviceContext.sendBroadcast(intent); return retValue; } /** * Create a new XMPP identity. Does not use the {@link XMPPConnectionManager} to establish an XMPP but creates its own connection * and closes it */ public String newMainIdentity(String client, String identifier, String domain, String password, long remoteCallId, String host) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("Identfier must be specified", null != identifier && identifier.length() > 0); Dbc.require("Domain must be specified", null != domain && domain.length() > 0); Dbc.require("Password must be specified", null != password && password.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "newMainIdentity identity: " + identifier + " domain: " + domain + " password: " + password + " for client: " + client); }; String retValue = null; //Send intent Intent intent = new Intent(NEW_MAIN_IDENTITY); if (this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); String serverHost = domain; if (null != host) { serverHost = host; } String serviceName = domain; Connection newIdConnection = null; try { ConnectionConfiguration config = new ConnectionConfiguration(serverHost, port, serviceName); newIdConnection = new XMPPConnection(config); newIdConnection.connect(); createAccount(newIdConnection, identifier, password); retValue = createFullUserJID(identifier, domain, this.resource); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Created user JID: " + retValue); }; //Send intent intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, retValue); } catch (XMPPException e) { Log.e(LOG_TAG, e.getMessage(), e); //Send intent intent = new Intent(XMPPAgent.NEW_MAIN_IDENTITY_EXCEPTION); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); createNotification("Error creating new identity: " + e.getMessage(), COMMS_CANNOT_CREATE_ID, NOTIFICATION_TITLE); } finally { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Create intent sent for: " + retValue); } this.serviceContext.sendBroadcast(intent); //ensure that XMPP connection is disconnected newIdConnection.disconnect(); } return null; } /** * Allow the XMPP server to be found with its IP address */ public String login(String client, String identifier, String domain, String password, String host, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("Identfier must be specified", null != identifier && identifier.length() > 0); Dbc.require("Domain must be specified", null != domain && domain.length() > 0); Dbc.require("Password must be specified", null != password && password.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "login identifier: " + identifier + " domain: " + domain + " password: " + password + " host: " + host + " for client: " + client); }; String retValue = null; Intent intent = new Intent(LOGIN); if (this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); XMPPConnectionProperties xmppConnectProps = new XMPPConnectionProperties(); xmppConnectProps.setDebug(this.debug); xmppConnectProps.setHostIP(host); xmppConnectProps.setNodeResource(this.resource); xmppConnectProps.setPassword(password); xmppConnectProps.setServiceName(domain); xmppConnectProps.setServicePort(this.port); xmppConnectProps.setUserName(identifier); this.xmppConnectMgr.enableConnection(xmppConnectProps, this.serviceContext, createFullUserJID(identifier, domain, this.resource), client, remoteCallId); return null; } /** * Allow the XMPP server to be found with its DNS resolved name */ public String login(String client, String identifier, String domain, String password, long remoteCallId) { this.login(client, identifier, domain, password, null, remoteCallId); return null; } public boolean logout(String client, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "logout for client: " + client); }; boolean retValue = false; Intent intent = new Intent(LOGOUT); if (this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, retValue); this.xmppConnectMgr.disableConnection(client, remoteCallId); return false; } public boolean destroyMainIdentity(String client, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "destroyMainIdentity for client: " + client); }; //Send intent Intent intent = new Intent(DESTROY_MAIN_IDENTITY); intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, false); if (this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); this.serviceContext.sendBroadcast(intent); return false; // http://code.google.com/p/asmack/issues/detail?id=63 // try { // connection.getAccountManager().deleteAccount(); // return true; // } catch (Exception e) { // Log.e(LOG_TAG, e.getMessage(), e); // return false; // } } @Override public boolean configureAgent(String client, String xmppDomainAuthorityNode, int xmppPort, String xmppResource, boolean xmppDebug, long remoteCallId) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("Domain Authority Node must be specified", null != xmppDomainAuthorityNode && xmppDomainAuthorityNode.length() > 0); Dbc.require("Port must be positive", xmppPort > 0); Dbc.require("Resource must be specified", null != xmppResource && xmppResource.length() > 0); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "configureAgent for client: " + client); }; if (null != this.androidCommsReceiver) { this.teardownBroadcastReceiver(this.androidCommsReceiver); } if (null != this.xmppConnectionReceiver) { this.teardownBroadcastReceiver(this.xmppConnectionReceiver); } this.androidCommsReceiver = this.setupAndroidCommsReceiver(); this.xmppConnectionReceiver = this.setupXMPPConnectionReceiver(); this.setDomainAuthorityNode(client, xmppDomainAuthorityNode); this.setPortNumber(client, xmppPort); this.setResource(client, xmppResource); this.setDebug(client, xmppDebug); //Send intent Intent intent = new Intent(CONFIGURE_AGENT); intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, true); if (this.restrictBroadcast) { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Restrict broadcast to package: " + client); }; intent.setPackage(client); } intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); this.serviceContext.sendBroadcast(intent); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Return Value broadcast sent: " + intent.getAction()); }; return false; } private void setDomainAuthorityNode(String client, String domainAuthorityNode) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("Domain Authority Node must be specified", null != domainAuthorityNode && domainAuthorityNode.length() > 0); this.domainAuthorityNode = domainAuthorityNode; } private void setPortNumber(String client, int port) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("Port must be positive", port > 0); this.port = port; } private void setResource(String client, String resource) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("Resource must be specified", null != resource && resource.length() > 0); this.resource = resource; } private void setDebug(String client, boolean enabled) { Dbc.require("Client must be specified", null != client && client.length() > 0); this.debug = enabled; } private void removeProviders(ProviderElementNamespaceRegistrar.ElementNamespaceTuple tuple) { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "removeProviders"); } ProviderManager.getInstance().removeIQProvider(tuple.elementName, tuple.namespace); ProviderManager.getInstance().removeExtensionProvider(tuple.elementName, tuple.namespace); } // private void connect() throws XMPPException { // if (DEBUG_LOGGING) { // Log.d(LOG_TAG, "connect"); // }; // // if(!connection.isConnected()) { // connection.connect(); // connection.login(username, password, resource); // } // usingConnectionCounter++; // } // private void disconnect() { // if (DEBUG_LOGGING) { // Log.d(LOG_TAG, "disconnect"); // }; // usingConnectionCounter--; // if(usingConnectionCounter == 0) // connection.disconnect(); // } private Packet createPacketFromXml(final String xml) { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "createPacketFromXml xml: " + xml); }; return new Packet() { @Override public String toXML() { return xml; } }; } private boolean isDiscoItem(IQ iq) throws SAXException, IOException, ParserConfigurationException { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "isDiscoItem"); }; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); Element element = factory.newDocumentBuilder().parse(new InputSource(new StringReader(iq.toXML()))).getDocumentElement(); if(element.getChildNodes().getLength() == 1) { Node query = element.getChildNodes().item(0); return query.getNodeName().equals("query") && query.lookupNamespaceURI(query.getPrefix()).equals(XMPPNode.ITEM_NAMESPACE); } else { return false; } } private static class NamespaceFilter implements PacketFilter { private List<String> namespaces; public NamespaceFilter(String[] namespaces) { this.namespaces = Arrays.asList(namespaces); } public boolean accept(Packet packet) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); Element element = factory.newDocumentBuilder().parse(new InputSource(new StringReader(packet.toXML()))).getDocumentElement(); NodeList childs = element.getChildNodes(); for(int i=0; i<childs.getLength(); i++) { Node child = childs.item(i); if(child instanceof Element) { Element childElem = (Element)child; String namespace = childElem.lookupNamespaceURI(childElem.getPrefix()); if(!namespace.equals("jabber:client") && !namespace.equals("jabber:server") && namespaces.contains(namespace)) return true; } } } catch (Exception e) { Log.e(LOG_TAG, e.getMessage(), e); } return false; } } private void createAccount(Connection connection, String username, String password) throws XMPPException { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "createAccount user: " + username + " password: " + password); }; AccountManager accountMgr = connection.getAccountManager(); Map<String, String> attributes = new HashMap<String, String>(); attributes.put("username", username); attributes.put("password", password); accountMgr.createAccount(username, password, attributes); } private static String createFullUserJID(String identifier, String domain, String jidResource) { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "username identifier: " + identifier + " domain: " + domain + " resource: " + jidResource); }; return identifier + "@" + domain + "/" + jidResource; } // /** // * Create XMPP Configuration object // * // * @param server DNS name of XMPP server // * @param username // * @param password // * @param host IP address of XMPP server (optional). If used, the DNS lookup to resolve the server is overridden. // */ // private void loadConfig(String server, String username, String password, String host) { // if (DEBUG_LOGGING) { // Log.d(LOG_TAG, "loadConfig server: " + server + " username: " + username + " password: " + password + " host: " + host); // }; // // this.username = username; // this.password = password; // String xmppHost = server; // if (null != host) { // xmppHost = host; // } // ConnectionConfiguration config = new ConnectionConfiguration(xmppHost, port, server); // // connection = new XMPPConnection(config); // // if(debug) { // connection.addPacketListener(new PacketListener() { // // public void processPacket(Packet packet) { // if (DEBUG_LOGGING) { // Log.d(LOG_TAG, "Packet received: " + packet.toXML()); // }; // } // // }, new PacketFilter() { // // public boolean accept(Packet packet) { // return true; // } // }); // connection.addPacketSendingListener(new PacketListener() { // // public void processPacket(Packet packet) { // if (DEBUG_LOGGING) { // Log.d(LOG_TAG, "Packet sent: " + packet.toXML()); // }; // } // // }, new PacketFilter() { // // public boolean accept(Packet packet) { // return true; // } // }); // } // } // private boolean isConnectedInternal() { // boolean retValue = false; // // if (null != connection) { // retValue = connection.isConnected(); // } // return retValue; // } private boolean UnRegisterCommManagerInternal() { Set<ProviderElementNamespaceRegistrar.ElementNamespaceTuple> tuples = providerRegistrar.getRegists(); for(ProviderElementNamespaceRegistrar.ElementNamespaceTuple tuple:tuples) { removeProviders(tuple); } providerRegistrar.clear(); return true; } // private boolean logoutInternal() { // UnRegisterCommManagerInternal(); // connection.disconnect(); // usingConnectionCounter = 0; // // return true; // } /** * Societies enabled aSmack Packet Listener * */ private class RegisterPacketListener implements PacketListener { String client; long remoteCallId; public RegisterPacketListener(String client, long remoteCallId) { this.client = client; this.remoteCallId = remoteCallId; } public void processPacket(Packet packet) { //Send intent Intent intent = new Intent(PUBSUB_EVENT); //TODO: Extra parameter required for eventual client component that //wants to receive this intent // if (AndroidCommsBase.this.restrictBroadcast) { // intent.setPackage(this.client); // } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, packet.toXML()); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, PUBSUB_EVENT_CALLBACK_ID); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Pubsub node intent sent: " + packet.toXML()); }; } } private class SendIQPacketListener implements PacketListener { String client; long remoteCallId; public SendIQPacketListener(String client, long remoteCallId) { this.client = client; this.remoteCallId = remoteCallId; } public void processPacket(Packet packet) { IQ iq = (IQ)packet; try { AndroidCommsBase.this.xmppConnectMgr.getValidConnection().removePacketListener(this); // disconnect(); if(iq.getType() == IQ.Type.RESULT) { //Send intent Intent intent = new Intent(SEND_IQ_RESULT); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(this.client); } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, packet.toXML()); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, this.remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); } else if(iq.getType() == IQ.Type.ERROR) { //Send intent Intent intent = new Intent(SEND_IQ_ERROR); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(this.client); } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, packet.toXML()); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, this.remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); createNotification("Error invoking remote method: " + packet.toXML(), COMMS_CANNOT_SEND_IQ, NOTIFICATION_TITLE); } } catch (NoXMPPConnectionAvailableException e) { Log.e(LOG_TAG, e.getMessage(), e); //Send intent Intent intent = new Intent(SEND_IQ_ERROR); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, ""); intent.putExtra(INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); } } } private class GetItemsPacketListener implements PacketListener { String client; long remoteCallId; public GetItemsPacketListener(String client, long remoteCallId) { this.client = client; this.remoteCallId = remoteCallId; } public void processPacket(Packet packet) { IQ iq = (IQ)packet; try { AndroidCommsBase.this.xmppConnectMgr.getValidConnection().removePacketListener(this); // disconnect(); if(iq.getType() == IQ.Type.RESULT) { if(isDiscoItem(iq)) { //Send intent Intent intent = new Intent(GET_ITEMS_RESULT); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(this.client); } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, packet.toXML()); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, this.remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); } } else if(iq.getType() == IQ.Type.ERROR) { //Send intent Intent intent = new Intent(GET_ITEMS_ERROR); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(this.client); } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, packet.toXML()); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, this.remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); } } catch (NoXMPPConnectionAvailableException e) { Log.e(LOG_TAG, e.getMessage(), e); //Send intent Intent intent = new Intent(GET_ITEMS_EXCEPTION); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, ""); intent.putExtra(INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); } catch (Exception e) { Log.e(LOG_TAG, e.getMessage(), e); //Send intent Intent intent = new Intent(GET_ITEMS_EXCEPTION); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, ""); intent.putExtra(INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); } } } /** * Create a String array of the exception trace * * @param e * @return String array */ private static String[] getStackTraceArray(Exception e){ StackTraceElement[] stackTraceElements = e.getStackTrace(); String[] stackTracelines = new String[stackTraceElements.length]; int i =0; for(StackTraceElement se : stackTraceElements){ stackTracelines[i++] = se.toString(); } return stackTracelines; } /** * Determine if an array of namespace elements relate to XMPP Pubsub * * @param namespaces * @return */ private static boolean isNameSpacePubsub(String [] namespaces) { boolean retValue = false; for (String element : namespaces) { if (element.contains(PUBSUB_NAMESPACE_KEY)) { retValue = true; break; } } return retValue; } /** * Create Android Notification * @param message * @param title * @param type (not dislpayed) */ private void createNotification(String message, String title, String type) { int notifierflags [] = new int [1]; notifierflags[0] = Notification.FLAG_AUTO_CANCEL; AndroidNotifier notifier = new AndroidNotifier(AndroidCommsBase.this.serviceContext, Notification.DEFAULT_SOUND, notifierflags); notifier.notifyMessage(message, type, this.getClass(), title); } /** * Create a broadcast receiver for monitoring Android Connectivity * * @return the created broadcast receiver */ private BroadcastReceiver setupAndroidCommsReceiver() { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Set up connectivity changes broadcast receiver"); }; BroadcastReceiver receiver = new AndroidCommsReceiver(); this.serviceContext.registerReceiver(receiver, createAndroidCommsIntentFilter()); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Register connectivity changes broadcast receiver"); }; return receiver; } /** * Create a broadcast receiver for monitoring XMPPConnection states * * @return the created broadcast receiver */ private BroadcastReceiver setupXMPPConnectionReceiver() { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Set up XMPP connection states broadcast receiver"); }; BroadcastReceiver receiver = new XMPPConnectionReceiver(); this.serviceContext.registerReceiver(receiver, createXMPPConnectionIntentFilter()); if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Register XMPP connection states broadcast receiver"); }; return receiver; } /** * Unregister the broadcast receiver */ private void teardownBroadcastReceiver(BroadcastReceiver receiver) { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "Tear down broadcast receiver"); }; this.serviceContext.unregisterReceiver(receiver); } /** * Create a suitable intent filter for monitoring Android connectivity * @return IntentFilter */ private IntentFilter createAndroidCommsIntentFilter() { //register broadcast receiver to receive SocietiesEvents return values IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); return intentFilter; } /** * Create a suitable intent filter for monitoring the XMPPConnection states * @return IntentFilter */ private IntentFilter createXMPPConnectionIntentFilter() { //register broadcast receiver to receive SocietiesEvents return values IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(IConnectionState.XMPP_CONNECTION_CHANGED); intentFilter.addAction(IConnectionState.XMPP_AUTHENTICATION_FAILURE); intentFilter.addAction(IConnectionState.XMPP_NO_NETWORK_FOUND_FAILURE); intentFilter.addAction(IConnectionState.XMPP_CONNECTIVITY_FAILURE); return intentFilter; } /** * Broadcast receiver to receive intent return values from ConnectionManager * * TODO: If base API increases extra notification information can be displayed * in the more detailed notification style */ private class AndroidCommsReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "AndroidCommsReceiver received action: " + intent.getAction()); } if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { boolean unConnected = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); if (unConnected) { ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); String networkType = null; if (null != activeNetwork) { networkType = activeNetwork.getTypeName(); } String reason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON); String extraInfo = intent.getStringExtra(ConnectivityManager.EXTRA_EXTRA_INFO); boolean failover = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false); // createNotification("Device has lost connectivity", COMMS_NO_CONNECTIVITY, NOTIFICATION_TITLE); } else { if (AndroidCommsBase.this.lostConnection) { createNotification("Device has regained connectivity", COMMS_RESTORED_CONNECTIVITY, NOTIFICATION_TITLE); AndroidCommsBase.this.lostConnection = false; } } } } } /** * Broadcast receiver to receive intent return values from {@link XMPPConnectionManager} * */ private class XMPPConnectionReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (DEBUG_LOGGING) { Log.d(LOG_TAG, "XMPPConnectionReceiver received action: " + intent.getAction()); } if (intent.getAction().equals(IConnectionState.XMPP_CONNECTION_CHANGED)) { if (intent.getIntExtra(IConnectionState.INTENT_CURRENT_CONNECTION_STATE, IConnectionState.INVALID_INTENT_INTEGER_EXTRA_VALUE) == ConnectionState.Connected.ordinal()) { //Send intent Intent sendIntent = new Intent(XMPPAgent.LOGIN); if (AndroidCommsBase.this.restrictBroadcast) { sendIntent.setPackage(intent.getStringExtra(IConnectionState.INTENT_REMOTE_CALL_CLIENT)); } sendIntent.putExtra(XMPPAgent.INTENT_RETURN_CALL_ID_KEY, intent.getLongExtra(IConnectionState.INTENT_REMOTE_CALL_ID, INVALID_LONG_INTENT_VALUE)); sendIntent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, intent.getStringExtra(IConnectionState.INTENT_REMOTE_USER_JID)); AndroidCommsBase.this.serviceContext.sendBroadcast(sendIntent); } else if (intent.getIntExtra(IConnectionState.INTENT_CURRENT_CONNECTION_STATE, IConnectionState.INVALID_INTENT_INTEGER_EXTRA_VALUE) == ConnectionState.Disconnected.ordinal()) { //Send intent Intent sendIntent = new Intent(XMPPAgent.LOGOUT); if (AndroidCommsBase.this.restrictBroadcast) { sendIntent.setPackage(intent.getStringExtra(IConnectionState.INTENT_REMOTE_CALL_CLIENT)); } sendIntent.putExtra(XMPPAgent.INTENT_RETURN_CALL_ID_KEY, intent.getLongExtra(IConnectionState.INTENT_REMOTE_CALL_ID, INVALID_LONG_INTENT_VALUE)); sendIntent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, true); AndroidCommsBase.this.serviceContext.sendBroadcast(sendIntent); } } else if (intent.getAction().equals(IConnectionState.XMPP_AUTHENTICATION_FAILURE) || intent.getAction().equals(IConnectionState.XMPP_CONNECTIVITY_FAILURE) || intent.getAction().equals(IConnectionState.XMPP_NO_NETWORK_FOUND_FAILURE)) { //Send intent Intent sendIntent = new Intent(XMPPAgent.LOGIN_EXCEPTION); if (AndroidCommsBase.this.restrictBroadcast) { sendIntent.setPackage(intent.getStringExtra(IConnectionState.INTENT_REMOTE_CALL_CLIENT)); } sendIntent.putExtra(XMPPAgent.INTENT_RETURN_CALL_ID_KEY, intent.getLongExtra(IConnectionState.INTENT_REMOTE_CALL_ID, INVALID_LONG_INTENT_VALUE)); sendIntent.putExtra(XMPPAgent.INTENT_RETURN_EXCEPTION_KEY, intent.getStringExtra(IConnectionState.INTENT_FAILURE_DESCRIPTION)); AndroidCommsBase.this.serviceContext.sendBroadcast(sendIntent); } } } private class queryVCardPacketListener implements PacketListener { String client; long remoteCallId; public queryVCardPacketListener(String client, long remoteCallId) { this.client = client; this.remoteCallId = remoteCallId; } public void processPacket(Packet packet) { IQ iq = (IQ)packet; try { AndroidCommsBase.this.xmppConnectMgr.getValidConnection().removePacketListener(this); if(iq.getType() == IQ.Type.RESULT) { //Send intent Intent intent = new Intent(GET_USER_VCARD); if (AndroidCommsBase.this.restrictBroadcast) intent.setPackage(this.client); //intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, packet.toXML()); VCard returnedCard = (VCard)packet; VCardParcel parcelVCard = VCardUtilities.convertToParcelVCard(returnedCard); intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, (Parcelable)parcelVCard); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, this.remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); } else if(iq.getType() == IQ.Type.ERROR) { //Send intent Intent intent = new Intent(GET_USER_VCARD); if (AndroidCommsBase.this.restrictBroadcast) intent.setPackage(this.client); intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, packet.toXML()); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, this.remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); createNotification("Error invoking remote method: " + packet.toXML(), COMMS_CANNOT_SEND_IQ, NOTIFICATION_TITLE); } } catch (NoXMPPConnectionAvailableException e) { Log.e(LOG_TAG, e.getMessage(), e); //Send intent Intent intent = new Intent(SEND_IQ_ERROR); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, ""); intent.putExtra(INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); } } } public void setVCard(String client, VCardParcel vCard) { //REQUIRED DUE TO ISSUE: https://code.google.com/p/asmack/issues/detail?id=14#c8 ProviderManager.getInstance().addIQProvider("vCard", "vcard-temp", new org.jivesoftware.smackx.provider.VCardProvider()); VCard xmppCard = VCardUtilities.convertToXMPPVCard(vCard); try { xmppCard.save(xmppConnectMgr.getValidConnection()); } catch (XMPPException e1) { e1.printStackTrace(); } catch (NoXMPPConnectionAvailableException e) { e.printStackTrace(); } } public VCardParcel getVCard(String client, long remoteCallId, String userId) { Dbc.require("Client must be specified", null != client && client.length() > 0); Dbc.require("userId must be specified", null != userId && userId.length() > 0); if (DEBUG_LOGGING) Log.d(LOG_TAG, "getVCard userId: " + userId + " for client: " + client); //REQUIRED DUE TO ISSUE: https://code.google.com/p/asmack/issues/detail?id=14#c8 ProviderManager.getInstance().addIQProvider("vCard", "vcard-temp", new org.jivesoftware.smackx.provider.VCardProvider()); String id = "123"; String xml = "<iq id='123' to='" + userId + "' type='get'><vCard xmlns='vcard-temp'/></iq>"; try { this.xmppConnectMgr.getValidConnection().addPacketListener(new queryVCardPacketListener(client, remoteCallId), new AndFilter(new PacketTypeFilter(IQ.class),new PacketIDFilter(id))); this.xmppConnectMgr.getValidConnection().sendPacket(createPacketFromXml(xml)); } catch (NoXMPPConnectionAvailableException e) { Log.e(LOG_TAG, e.getMessage(), e); //Send intent Intent intent = new Intent(SEND_IQ_EXCEPTION); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); createNotification("Error invoking remote method: " + e.getMessage(), COMMS_CANNOT_SEND_IQ, NOTIFICATION_TITLE); } catch (Exception e) { Log.e(LOG_TAG, e.getMessage(), e); //Send intent Intent intent = new Intent(SEND_IQ_EXCEPTION); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(INTENT_RETURN_EXCEPTION_KEY, e.getMessage()); intent.putExtra(INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); createNotification("Error invoking remote method: " + e.getMessage(), COMMS_CANNOT_SEND_IQ, NOTIFICATION_TITLE); } return null; } public VCardParcel getVCard(String client, long remoteCallId) { //REQUIRED DUE TO ISSUE: https://code.google.com/p/asmack/issues/detail?id=14#c8 ProviderManager.getInstance().addIQProvider("vCard", "vcard-temp", new org.jivesoftware.smackx.provider.VCardProvider()); VCardParcel parcelVCard = null; try { VCard returnedCard = new VCard(); xmppConnectMgr.getValidConnection().connect(); //AUTHENTICATED CONNECTION returnedCard.load(xmppConnectMgr.getValidConnection()); parcelVCard = VCardUtilities.convertToParcelVCard(returnedCard); //RETURN INTENT Intent intent = new Intent(XMPPAgent.GET_VCARD); if (AndroidCommsBase.this.restrictBroadcast) intent.setPackage(client); intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, (Parcelable)parcelVCard); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); this.serviceContext.sendBroadcast(intent); } catch (XMPPException e) { e.printStackTrace(); } catch (NoXMPPConnectionAvailableException e) { Log.e(LOG_TAG, e.getMessage(), e); //Send intent Intent intent = new Intent(GET_VCARD); if (AndroidCommsBase.this.restrictBroadcast) { intent.setPackage(client); } intent.putExtra(XMPPAgent.INTENT_RETURN_VALUE_KEY, (Parcelable)parcelVCard); intent.putExtra(INTENT_RETURN_EXCEPTION_KEY, "NoXMPPConnectionAvailableException"); intent.putExtra(INTENT_RETURN_EXCEPTION_TRACE_KEY, getStackTraceArray(e)); intent.putExtra(INTENT_RETURN_CALL_ID_KEY, remoteCallId); AndroidCommsBase.this.serviceContext.sendBroadcast(intent); } return null; } }