/** 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 (IBM), 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.remote.helper; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import org.societies.android.api.common.ADate; import org.societies.android.api.comms.IMethodCallback; import org.societies.android.api.context.ContextClientInvocationException; import org.societies.android.api.context.ContextClientNotConnectedException; import org.societies.android.api.context.CtxException; import org.societies.android.api.context.ICtxClient; import org.societies.android.api.context.ICtxClientCallback; import org.societies.android.api.context.ICtxClientHelper; import org.societies.android.api.privacytrust.trust.ITrustClient; import org.societies.android.api.services.ICoreSocietiesServices; import org.societies.android.api.utilities.ServiceMethodTranslator; import org.societies.api.schema.identity.RequestorBean; import org.societies.api.schema.context.model.CtxAssociationBean; import org.societies.api.schema.context.model.CtxAttributeBean; import org.societies.api.schema.context.model.CtxAttributeIdentifierBean; import org.societies.api.schema.context.model.CtxEntityBean; import org.societies.api.schema.context.model.CtxEntityIdentifierBean; import org.societies.api.schema.context.model.CtxIdentifierBean; import org.societies.api.schema.context.model.CtxModelObjectBean; import org.societies.api.schema.context.model.CtxModelTypeBean; 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.Bundle; import android.os.IBinder; import android.os.Messenger; import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; /** * This class provides a simple callback interface to the Societies Android Context service * It assumes that the service has already been bound to the Android Comms and Pubsub services. * * @author <a href="mailto:pkosmidis@cn.ntua.gr">Pavlos Kosmides</a> (ICCS) * @since 1.1 */ public class ContextClientHelper implements ICtxClientHelper { private final static String LOG_TAG = ContextClientHelper.class.getName(); private enum classMethods { createEntity, createAttribute, createAssociation, lookup, lookupEntities, remove, retrieve, retrieveIndividualEntityId, retrieveCommunityEntityId, update } private Context context; private String clientPackageName; private boolean connectedToContextClient; private Messenger targetService; private BroadcastReceiver receiver; private IMethodCallback startupCallback; //Array of queues to store callbacks for each method //Assumption is that similar method calls will be answered in correct order //as remote service call is via Messenger mechanism which is itself queue oriented private ConcurrentLinkedQueue<ICtxClientCallback> methodQueues []; /** * Default constructor * @param context */ @SuppressWarnings("unchecked") public ContextClientHelper(Context context) { this.context = context; this.clientPackageName = this.context.getApplicationContext().getPackageName(); this.connectedToContextClient = false; this.targetService = null; this.methodQueues = (ConcurrentLinkedQueue<ICtxClientCallback>[])new ConcurrentLinkedQueue[classMethods.values().length]; this.startupCallback = null; } @Override public boolean setUpService(IMethodCallback callback) { if (callback == null) throw new NullPointerException("callback can't be null"); Log.d(LOG_TAG, "setUpService"); if (!this.connectedToContextClient) { this.setupBroadcastReceiver(); this.startupCallback = callback; Intent serviceIntent = new Intent(ICoreSocietiesServices.CONTEXT_SERVICE_INTENT); this.context.bindService(serviceIntent, this.contextClientConnection, Context.BIND_AUTO_CREATE); } return false; } @Override public boolean tearDownService(IMethodCallback callback) { if (callback == null) throw new NullPointerException("callback can't be null"); Log.d(LOG_TAG, "tearDownService"); if (this.connectedToContextClient) { this.teardownBroadcastReceiver(); this.context.unbindService(this.contextClientConnection); Log.d(LOG_TAG, "tearDownService completed"); callback.returnAction(true); } return false; } @Override public CtxEntityBean createEntity(RequestorBean requestor, String targetCss, String type, ICtxClientCallback callback) throws CtxException { if (requestor == null) throw new NullPointerException("requestor can't be null"); if (targetCss == null) throw new NullPointerException("targetCSS can't be null"); if (callback == null) throw new NullPointerException("callback can't be null"); Log.d(LOG_TAG, "createEntity with requestor=" + requestor.getRequestorId() + ", targetCss=" + targetCss + ", type=" + type + ", callback=" + callback); if (this.connectedToContextClient) { //Add callback class to method queue tail this.initialiseQueue(classMethods.createEntity.ordinal()); this.methodQueues[classMethods.createEntity.ordinal()].add(callback); //Select target method and create message to convey remote invocation String targetMethod = ICtxClient.methodsArray[0]; android.os.Message outMessage = android.os.Message.obtain(null, ServiceMethodTranslator.getMethodIndex( ICtxClient.methodsArray, targetMethod), 0, 0); Bundle outBundle = new Bundle(); outBundle.putString(ServiceMethodTranslator.getMethodParameterName( targetMethod, 0), this.clientPackageName); Log.d(LOG_TAG, "client: " + this.clientPackageName); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName( targetMethod, 1), requestor); Log.d(LOG_TAG, "requestor: " + requestor.getRequestorId()); outBundle.putString(ServiceMethodTranslator.getMethodParameterName(targetMethod, 2), targetCss); Log.d(LOG_TAG, "targetCss: " + targetCss); outBundle.putString(ServiceMethodTranslator.getMethodParameterName(targetMethod, 3), type); Log.d(LOG_TAG, "type: " + type); outMessage.setData(outBundle); Log.d(LOG_TAG, "Call service method: " + targetMethod); try { this.targetService.send(outMessage); } catch (RemoteException e) { Log.e(LOG_TAG, "Could not send remote method invocation", e); //Retrieve callback and signal failure final ICtxClientCallback retrievedCallback = this.methodQueues[classMethods.createEntity.ordinal()].poll(); if (null != retrievedCallback) { retrievedCallback.onException(new ContextClientInvocationException()); } } } else { Log.e(LOG_TAG, "Note connected to Context Client service"); callback.onException(new ContextClientNotConnectedException()); } return null; } @Override public CtxAttributeBean createAttribute(RequestorBean requestor, CtxEntityIdentifierBean scope, String type, ICtxClientCallback callback) throws CtxException { if (requestor == null) throw new NullPointerException("requestor can't be null"); if (scope == null) throw new NullPointerException("scope can't be null"); if (callback == null) throw new NullPointerException("callback can't be null"); Log.d(LOG_TAG, "createAttribute with requestor=" + requestor.getRequestorId() + ", scope=" + scope.toString() + ", type=" + type + ", callback=" + callback); if (this.connectedToContextClient) { //Add callback class to method queue tail this.initialiseQueue(classMethods.createAttribute.ordinal()); this.methodQueues[classMethods.createAttribute.ordinal()].add(callback); //Select target method and create message to convey remote invocation String targetMethod = ICtxClient.methodsArray[1]; android.os.Message outMessage = android.os.Message.obtain(null, ServiceMethodTranslator.getMethodIndex( ICtxClient.methodsArray, targetMethod), 0, 0); Bundle outBundle = new Bundle(); outBundle.putString(ServiceMethodTranslator.getMethodParameterName( targetMethod, 0), this.clientPackageName); Log.d(LOG_TAG, "client: " + this.clientPackageName); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName( targetMethod, 1), requestor); Log.d(LOG_TAG, "requestor: " + requestor.getRequestorId()); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName(targetMethod, 2), scope); Log.d(LOG_TAG, "scope: " + scope.toString()); outBundle.putString(ServiceMethodTranslator.getMethodParameterName(targetMethod, 3), type); Log.d(LOG_TAG, "type: " + type); outMessage.setData(outBundle); Log.d(LOG_TAG, "Call service method: " + targetMethod); try { this.targetService.send(outMessage); } catch (RemoteException e) { Log.e(LOG_TAG, "Could not send remote method invocation", e); //Retrieve callback and signal failure final ICtxClientCallback retrievedCallback = this.methodQueues[classMethods.createAttribute.ordinal()].poll(); if (null != retrievedCallback) { retrievedCallback.onException(new ContextClientInvocationException()); } } } else { Log.e(LOG_TAG, "Note connected to Context Client service"); callback.onException(new ContextClientNotConnectedException()); } return null; } @Override public CtxAssociationBean createAssociation(RequestorBean requestor, String targetCss, String type, ICtxClientCallback callback) throws CtxException { if (requestor == null) throw new NullPointerException("requestor can't be null"); if (targetCss == null) throw new NullPointerException("targetCSS can't be null"); if (callback == null) throw new NullPointerException("callback can't be null"); Log.d(LOG_TAG, "createAssociation with requestor=" + requestor.getRequestorId() + ", targetCss=" + targetCss + ", type=" + type + ", callback=" + callback); if (this.connectedToContextClient) { //Add callback class to method queue tail this.initialiseQueue(classMethods.createAssociation.ordinal()); this.methodQueues[classMethods.createAssociation.ordinal()].add(callback); //Select target method and create message to convey remote invocation String targetMethod = ICtxClient.methodsArray[2]; android.os.Message outMessage = android.os.Message.obtain(null, ServiceMethodTranslator.getMethodIndex( ICtxClient.methodsArray, targetMethod), 0, 0); Bundle outBundle = new Bundle(); outBundle.putString(ServiceMethodTranslator.getMethodParameterName( targetMethod, 0), this.clientPackageName); Log.d(LOG_TAG, "client: " + this.clientPackageName); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName( targetMethod, 1), requestor); Log.d(LOG_TAG, "requestor: " + requestor.getRequestorId()); outBundle.putString(ServiceMethodTranslator.getMethodParameterName(targetMethod, 2), targetCss); Log.d(LOG_TAG, "targetCss: " + targetCss); outBundle.putString(ServiceMethodTranslator.getMethodParameterName(targetMethod, 3), type); Log.d(LOG_TAG, "type: " + type); outMessage.setData(outBundle); Log.d(LOG_TAG, "Call service method: " + targetMethod); try { this.targetService.send(outMessage); } catch (RemoteException e) { Log.e(LOG_TAG, "Could not send remote method invocation", e); //Retrieve callback and signal failure final ICtxClientCallback retrievedCallback = this.methodQueues[classMethods.createAssociation.ordinal()].poll(); if (null != retrievedCallback) { retrievedCallback.onException(new ContextClientInvocationException()); } } } else { Log.e(LOG_TAG, "Note connected to Context Client service"); callback.onException(new ContextClientNotConnectedException()); } return null; } @Override public List<CtxIdentifierBean> lookup(RequestorBean requestor, String target, CtxModelTypeBean modelType, String type, ICtxClientCallback callback) throws CtxException { if (requestor == null) throw new NullPointerException("requestor can't be null"); if (target == null) throw new NullPointerException("targetCSS can't be null"); if (modelType == null) throw new NullPointerException("modelType can't be null"); if (callback == null) throw new NullPointerException("callback can't be null"); Log.d(LOG_TAG, "lookup with requestor=" + requestor.getRequestorId() + ", target=" + target + ", modelType=" + modelType.toString() +", type=" + type + ", callback=" + callback); if (this.connectedToContextClient) { //Add callback class to method queue tail this.initialiseQueue(classMethods.lookup.ordinal()); this.methodQueues[classMethods.lookup.ordinal()].add(callback); //Select target method and create message to convey remote invocation String targetMethod = ICtxClient.methodsArray[3]; android.os.Message outMessage = android.os.Message.obtain(null, ServiceMethodTranslator.getMethodIndex( ICtxClient.methodsArray, targetMethod), 0, 0); Bundle outBundle = new Bundle(); outBundle.putString(ServiceMethodTranslator.getMethodParameterName( targetMethod, 0), this.clientPackageName); Log.d(LOG_TAG, "client: " + this.clientPackageName); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName( targetMethod, 1), requestor); Log.d(LOG_TAG, "requestor: " + requestor.getRequestorId()); outBundle.putString(ServiceMethodTranslator.getMethodParameterName(targetMethod, 2), target); Log.d(LOG_TAG, "target: " + target); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName( targetMethod, 3), modelType); Log.d(LOG_TAG, "modelType: " + modelType.toString()); outBundle.putString(ServiceMethodTranslator.getMethodParameterName(targetMethod, 4), type); Log.d(LOG_TAG, "type: " + type); outMessage.setData(outBundle); Log.d(LOG_TAG, "Call service method: " + targetMethod); try { this.targetService.send(outMessage); } catch (RemoteException e) { Log.e(LOG_TAG, "Could not send remote method invocation", e); //Retrieve callback and signal failure final ICtxClientCallback retrievedCallback = this.methodQueues[classMethods.lookup.ordinal()].poll(); if (null != retrievedCallback) { retrievedCallback.onException(new ContextClientInvocationException()); } } } else { Log.e(LOG_TAG, "Note connected to Context Client service"); callback.onException(new ContextClientNotConnectedException()); } return null; } @Override public List<CtxIdentifierBean> lookup(RequestorBean requestor, CtxEntityIdentifierBean entityId, CtxModelTypeBean modelType, String type, ICtxClientCallback callback) throws CtxException { if (requestor == null) throw new NullPointerException("requestor can't be null"); if (entityId == null) throw new NullPointerException("entityId can't be null"); if (modelType == null) throw new NullPointerException("modelType can't be null"); if (callback == null) throw new NullPointerException("callback can't be null"); Log.d(LOG_TAG, "lookup with requestor=" + requestor.getRequestorId() + ", entityId=" + entityId + ", modelType=" + modelType.toString() +", type=" + type + ", callback=" + callback); if (this.connectedToContextClient) { //Add callback class to method queue tail this.initialiseQueue(classMethods.lookup.ordinal()); this.methodQueues[classMethods.lookup.ordinal()].add(callback); //Select target method and create message to convey remote invocation String targetMethod = ICtxClient.methodsArray[4]; android.os.Message outMessage = android.os.Message.obtain(null, ServiceMethodTranslator.getMethodIndex( ICtxClient.methodsArray, targetMethod), 0, 0); Bundle outBundle = new Bundle(); outBundle.putString(ServiceMethodTranslator.getMethodParameterName( targetMethod, 0), this.clientPackageName); Log.d(LOG_TAG, "client: " + this.clientPackageName); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName( targetMethod, 1), requestor); Log.d(LOG_TAG, "requestor: " + requestor.getRequestorId()); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName(targetMethod, 2), entityId); Log.d(LOG_TAG, "entityId: " + entityId); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName( targetMethod, 3), modelType); Log.d(LOG_TAG, "modelType: " + modelType.toString()); outBundle.putString(ServiceMethodTranslator.getMethodParameterName(targetMethod, 4), type); Log.d(LOG_TAG, "type: " + type); outMessage.setData(outBundle); Log.d(LOG_TAG, "Call service method: " + targetMethod); try { this.targetService.send(outMessage); } catch (RemoteException e) { Log.e(LOG_TAG, "Could not send remote method invocation", e); //Retrieve callback and signal failure final ICtxClientCallback retrievedCallback = this.methodQueues[classMethods.lookup.ordinal()].poll(); if (null != retrievedCallback) { retrievedCallback.onException(new ContextClientInvocationException()); } } } else { Log.e(LOG_TAG, "Note connected to Context Client service"); callback.onException(new ContextClientNotConnectedException()); } return null; } @Override public List<CtxEntityIdentifierBean> lookupEntities( RequestorBean requestor, String targetCss, String entityType, String attribType, Serializable minAttribValue, Serializable maxAttribValue, ICtxClientCallback callback) throws CtxException { // TODO Auto-generated method stub return null; } @Override public CtxModelObjectBean remove(RequestorBean requestor, CtxIdentifierBean identifier, ICtxClientCallback callback) throws CtxException { // TODO Auto-generated method stub return null; } @Override public CtxModelObjectBean retrieve(RequestorBean requestor, CtxIdentifierBean identifier, ICtxClientCallback callback) throws CtxException { if (requestor == null) throw new NullPointerException("requestor can't be null"); if (identifier == null) throw new NullPointerException("identifier can't be null"); if (callback == null) throw new NullPointerException("callback can't be null"); Log.d(LOG_TAG, "retrieve with requestor=" + requestor.getRequestorId() + ", identifier=" + identifier.toString() + ", callback=" + callback); if (this.connectedToContextClient) { //Add callback class to method queue tail this.initialiseQueue(classMethods.retrieve.ordinal()); this.methodQueues[classMethods.retrieve.ordinal()].add(callback); //Select target method and create message to convey remote invocation String targetMethod = ICtxClient.methodsArray[7]; android.os.Message outMessage = android.os.Message.obtain(null, ServiceMethodTranslator.getMethodIndex( ICtxClient.methodsArray, targetMethod), 0, 0); Bundle outBundle = new Bundle(); outBundle.putString(ServiceMethodTranslator.getMethodParameterName( targetMethod, 0), this.clientPackageName); Log.d(LOG_TAG, "client: " + this.clientPackageName); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName( targetMethod, 1), requestor); Log.d(LOG_TAG, "requestor: " + requestor.getRequestorId()); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName( targetMethod, 2), identifier); Log.d(LOG_TAG, "identifier: " + identifier.toString()); outMessage.setData(outBundle); Log.d(LOG_TAG, "Call service method: " + targetMethod); try { this.targetService.send(outMessage); } catch (RemoteException e) { Log.e(LOG_TAG, "Could not send remote method invocation", e); //Retrieve callback and signal failure final ICtxClientCallback retrievedCallback = this.methodQueues[classMethods.retrieve.ordinal()].poll(); if (null != retrievedCallback) { retrievedCallback.onException(new ContextClientInvocationException()); } } } else { Log.e(LOG_TAG, "Note connected to Context Client service"); callback.onException(new ContextClientNotConnectedException()); } return null; } @Override public CtxEntityIdentifierBean retrieveIndividualEntityId( RequestorBean requestor, String cssId, ICtxClientCallback callback) throws CtxException { if (requestor == null) throw new NullPointerException("requestor can't be null"); if (cssId == null) throw new NullPointerException("cssId can't be null"); if (callback == null) throw new NullPointerException("callback can't be null"); Log.d(LOG_TAG, "retrieveIndividualEntityId with requestor=" + requestor.getRequestorId() + ", cssIs=" + cssId + ", callback=" + callback); if (this.connectedToContextClient) { //Add callback class to method queue tail this.initialiseQueue(classMethods.retrieveIndividualEntityId.ordinal()); this.methodQueues[classMethods.retrieveIndividualEntityId.ordinal()].add(callback); //Select target method and create message to convey remote invocation String targetMethod = ICtxClient.methodsArray[8]; android.os.Message outMessage = android.os.Message.obtain(null, ServiceMethodTranslator.getMethodIndex( ICtxClient.methodsArray, targetMethod), 0, 0); Bundle outBundle = new Bundle(); outBundle.putString(ServiceMethodTranslator.getMethodParameterName( targetMethod, 0), this.clientPackageName); Log.d(LOG_TAG, "client: " + this.clientPackageName); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName( targetMethod, 1), requestor); Log.d(LOG_TAG, "requestor: " + requestor.getRequestorId()); outBundle.putString(ServiceMethodTranslator.getMethodParameterName(targetMethod, 2), cssId); Log.d(LOG_TAG, "cssId: " + cssId); outMessage.setData(outBundle); Log.d(LOG_TAG, "Call service method: " + targetMethod); try { this.targetService.send(outMessage); } catch (RemoteException e) { Log.e(LOG_TAG, "Could not send remote method invocation", e); //Retrieve callback and signal failure final ICtxClientCallback retrievedCallback = this.methodQueues[classMethods.retrieveIndividualEntityId.ordinal()].poll(); if (null != retrievedCallback) { retrievedCallback.onException(new ContextClientInvocationException()); } } } else { Log.e(LOG_TAG, "Note connected to Context Client service"); callback.onException(new ContextClientNotConnectedException()); } return null; } @Override public CtxEntityIdentifierBean retrieveCommunityEntityId( RequestorBean requestor, String cisId, ICtxClientCallback callback) throws CtxException { // TODO Auto-generated method stub return null; } @Override public CtxModelObjectBean update(RequestorBean requestor, CtxModelObjectBean object, ICtxClientCallback callback) throws CtxException { if (requestor == null) throw new NullPointerException("requestor can't be null"); if (object == null) throw new NullPointerException("cssId can't be null"); if (callback == null) throw new NullPointerException("callback can't be null"); Log.d(LOG_TAG, "update with requestor=" + requestor.getRequestorId() + ", object=" + object + ", callback=" + callback); if (this.connectedToContextClient) { //Add callback class to method queue tail this.initialiseQueue(classMethods.update.ordinal()); this.methodQueues[classMethods.update.ordinal()].add(callback); //Select target method and create message to convey remote invocation String targetMethod = ICtxClient.methodsArray[10]; android.os.Message outMessage = android.os.Message.obtain(null, ServiceMethodTranslator.getMethodIndex( ICtxClient.methodsArray, targetMethod), 0, 0); Bundle outBundle = new Bundle(); outBundle.putString(ServiceMethodTranslator.getMethodParameterName( targetMethod, 0), this.clientPackageName); Log.d(LOG_TAG, "client: " + this.clientPackageName); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName( targetMethod, 1), requestor); Log.d(LOG_TAG, "requestor: " + requestor.getRequestorId()); outBundle.putParcelable(ServiceMethodTranslator.getMethodParameterName(targetMethod, 2), object); Log.d(LOG_TAG, "object: " + object); outMessage.setData(outBundle); Log.d(LOG_TAG, "Call service method: " + targetMethod); try { this.targetService.send(outMessage); } catch (RemoteException e) { Log.e(LOG_TAG, "Could not send remote method invocation", e); //Retrieve callback and signal failure final ICtxClientCallback retrievedCallback = this.methodQueues[classMethods.update.ordinal()].poll(); if (null != retrievedCallback) { retrievedCallback.onException(new ContextClientInvocationException()); } } } else { Log.e(LOG_TAG, "Note connected to Context Client service"); callback.onException(new ContextClientNotConnectedException()); } return null; } /** * Context client service connection * * N.B. Unbinding from service does not callback. onServiceDisconnected is called back * if service connection lost */ private ServiceConnection contextClientConnection = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { Log.d(LOG_TAG, "Disconnecting from Context Client service"); //Unregister broadcast receiver if service binding broken. Otherwise //the next set-up of the service will one extra receiver causing possible problems. ContextClientHelper.this.teardownBroadcastReceiver(); connectedToContextClient = false; } public void onServiceConnected(ComponentName name, IBinder service) { Log.d(LOG_TAG, "Connecting to Context Client service"); connectedToContextClient = true; //get a remote binder ContextClientHelper.this.targetService = new Messenger(service); Log.d(LOG_TAG, "Target service " + name.getShortClassName() + " acquired: " + ContextClientHelper.this.targetService.getClass().getName()); Log.d(LOG_TAG, "Retrieve setup callback"); if (null != ContextClientHelper.this.startupCallback) { Log.d(LOG_TAG, "Setup callback valid"); ContextClientHelper.this.startupCallback.returnAction(true); } } }; /** * Broadcast receiver to receive intent return values from service method calls * Essentially this receiver invokes callbacks for relevant intents received from Android Communications. * Since more than one instance of this class can exist for an app, i.e. more than one component could be communicating, * callback IDs or queues cannot be assumed to exist for a particular Broadcast receiver. */ private class ContextClientHelperReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.d(LOG_TAG, "Received action: " + intent.getAction()); if (intent.getAction().equals(ICtxClient.CREATE_ASSOCIATION)) { if (null != ContextClientHelper.this.methodQueues[classMethods.createAssociation.ordinal()]) { ICtxClientCallback retrievedCallback = ContextClientHelper.this.methodQueues[classMethods.createAssociation.ordinal()].poll(); if (null != retrievedCallback) { final String exceptionMessage = intent.getStringExtra(ICtxClient.INTENT_EXCEPTION_VALUE_KEY); if (exceptionMessage != null) { retrievedCallback.onException(new ContextClientInvocationException(exceptionMessage)); } final CtxAssociationBean ctxAssociation; final Parcelable pCtxAssociation = (Parcelable) intent.getParcelableExtra(ICtxClient.INTENT_RETURN_VALUE_KEY); if (pCtxAssociation instanceof CtxAssociationBean) { ctxAssociation = (CtxAssociationBean) pCtxAssociation; } else { retrievedCallback.onException(new ContextClientInvocationException ("Unexpected return value type: " + ((pCtxAssociation != null) ? pCtxAssociation.getClass() : "null"))); return; } retrievedCallback.onCreatedAssociation(ctxAssociation); } else { Log.e(LOG_TAG, "Could not find callback for received action " + intent.getAction()); } } else { Log.e(LOG_TAG, "Could not find callback method queue for received action " + intent.getAction()); } } else if (intent.getAction().equals(ICtxClient.CREATE_ATTRIBUTE)) { if (null != ContextClientHelper.this.methodQueues[classMethods.createAttribute.ordinal()]) { ICtxClientCallback retrievedCallback = ContextClientHelper.this.methodQueues[classMethods.createAttribute.ordinal()].poll(); if (null != retrievedCallback) { final String exceptionMessage = intent.getStringExtra(ICtxClient.INTENT_EXCEPTION_VALUE_KEY); if (exceptionMessage != null) { retrievedCallback.onException(new ContextClientInvocationException(exceptionMessage)); } final CtxAttributeBean ctxAttribute; final Parcelable pCtxAttribute = (Parcelable) intent.getParcelableExtra(ICtxClient.INTENT_RETURN_VALUE_KEY); if (pCtxAttribute instanceof CtxAttributeBean) { ctxAttribute = (CtxAttributeBean) pCtxAttribute; } else { retrievedCallback.onException(new ContextClientInvocationException ("Unexpected return value type: " + ((pCtxAttribute != null) ? pCtxAttribute.getClass() : "null"))); return; } retrievedCallback.onCreatedAttribute(ctxAttribute); } else { Log.e(LOG_TAG, "Could not find callback for received action " + intent.getAction()); } } else { Log.e(LOG_TAG, "Could not find callback method queue for received action " + intent.getAction()); } } else if (intent.getAction().equals(ICtxClient.CREATE_ENTITY)) { if (null != ContextClientHelper.this.methodQueues[classMethods.createEntity.ordinal()]) { ICtxClientCallback retrievedCallback = ContextClientHelper.this.methodQueues[classMethods.createEntity.ordinal()].poll(); if (null != retrievedCallback) { final String exceptionMessage = intent.getStringExtra(ICtxClient.INTENT_EXCEPTION_VALUE_KEY); if (exceptionMessage != null) { retrievedCallback.onException(new ContextClientInvocationException(exceptionMessage)); } final CtxEntityBean ctxEntity; final Parcelable pCtxEntity = (Parcelable) intent.getParcelableExtra(ICtxClient.INTENT_RETURN_VALUE_KEY); if (pCtxEntity instanceof CtxEntityBean) { ctxEntity = (CtxEntityBean) pCtxEntity; } else { retrievedCallback.onException(new ContextClientInvocationException ("Unexpected return value type: " + ((pCtxEntity != null) ? pCtxEntity.getClass() : "null"))); return; } retrievedCallback.onCreatedEntity(ctxEntity); } else { Log.e(LOG_TAG, "Could not find callback for received action " + intent.getAction()); } } else { Log.e(LOG_TAG, "Could not find callback method queue for received action " + intent.getAction()); } } else if (intent.getAction().equals(ICtxClient.LOOKUP)) { if (null != ContextClientHelper.this.methodQueues[classMethods.lookup.ordinal()]) { ICtxClientCallback retrievedCallback = ContextClientHelper.this.methodQueues[classMethods.lookup.ordinal()].poll(); if (null != retrievedCallback) { final String exceptionMessage = intent.getStringExtra(ICtxClient.INTENT_EXCEPTION_VALUE_KEY); if (exceptionMessage != null) { retrievedCallback.onException(new ContextClientInvocationException(exceptionMessage)); } final List<CtxIdentifierBean> ctxIdsList = new ArrayList<CtxIdentifierBean>(); // List<Parcelable> pCtxIdsList = new ArrayList<Parcelable>(); final Parcelable[] pCtxIdsList = (Parcelable[]) intent.getParcelableArrayExtra(ICtxClient.INTENT_RETURN_VALUE_KEY); for (final Parcelable pCtxIdList : pCtxIdsList) { if (pCtxIdList instanceof CtxIdentifierBean) { ctxIdsList.add((CtxIdentifierBean) pCtxIdList); } else { retrievedCallback.onException(new ContextClientInvocationException ("Unexpected return value type: " + ((pCtxIdList != null) ? pCtxIdList.getClass() : "null"))); return; } } retrievedCallback.onLookupCallback(ctxIdsList); } else { Log.e(LOG_TAG, "Could not find callback for received action " + intent.getAction()); } } else { Log.e(LOG_TAG, "Could not find callback method queue for received action " + intent.getAction()); } } else if (intent.getAction().equals(ICtxClient.RETRIEVE)) { if (null != ContextClientHelper.this.methodQueues[classMethods.retrieve.ordinal()]) { ICtxClientCallback retrievedCallback = ContextClientHelper.this.methodQueues[classMethods.retrieve.ordinal()].poll(); if (null != retrievedCallback) { final String exceptionMessage = intent.getStringExtra(ICtxClient.INTENT_EXCEPTION_VALUE_KEY); if (exceptionMessage != null) { retrievedCallback.onException(new ContextClientInvocationException(exceptionMessage)); } final CtxModelObjectBean modelObject; final Parcelable pModelObject = (Parcelable) intent.getParcelableExtra(ICtxClient.INTENT_RETURN_VALUE_KEY); if (pModelObject instanceof CtxModelObjectBean) { modelObject = (CtxModelObjectBean) pModelObject; } else { retrievedCallback.onException(new ContextClientInvocationException ("Unexpected return value type: " + ((pModelObject != null) ? pModelObject.getClass() : "null"))); return; } retrievedCallback.onRetrieveCtx(modelObject); } else { Log.e(LOG_TAG, "Could not find callback for received action " + intent.getAction()); } } else { Log.e(LOG_TAG, "Could not find callback method queue for received action " + intent.getAction()); } } else if (intent.getAction().equals(ICtxClient.RETRIEVE_INDIVIDUAL_ENTITY_ID)) { if (null != ContextClientHelper.this.methodQueues[classMethods.retrieveIndividualEntityId.ordinal()]) { ICtxClientCallback retrievedCallback = ContextClientHelper.this.methodQueues[classMethods.retrieveIndividualEntityId.ordinal()].poll(); if (null != retrievedCallback) { final String exceptionMessage = intent.getStringExtra(ICtxClient.INTENT_EXCEPTION_VALUE_KEY); if (exceptionMessage != null) { retrievedCallback.onException(new ContextClientInvocationException(exceptionMessage)); } final CtxEntityIdentifierBean retrievedEntityId; final Parcelable pRetrievedEntityId = (Parcelable) intent.getParcelableExtra(ICtxClient.INTENT_RETURN_VALUE_KEY); if (pRetrievedEntityId instanceof CtxEntityIdentifierBean) { retrievedEntityId = (CtxEntityIdentifierBean) pRetrievedEntityId; } else { retrievedCallback.onException(new ContextClientInvocationException("Unexpected return value type: " + ((pRetrievedEntityId != null) ? pRetrievedEntityId.getClass() : "null"))); return; } retrievedCallback.onRetrievedEntityId(retrievedEntityId); } else { Log.e(LOG_TAG, "Could not find callback for received action " + intent.getAction()); } } else { Log.e(LOG_TAG, "Could not find callback method queue for received action " + intent.getAction()); } } else if (intent.getAction().equals(ICtxClient.UPDATE)) { if (null != ContextClientHelper.this.methodQueues[classMethods.update.ordinal()]) { ICtxClientCallback retrievedCallback = ContextClientHelper.this.methodQueues[classMethods.update.ordinal()].poll(); if (null != retrievedCallback) { final String exceptionMessage = intent.getStringExtra(ICtxClient.INTENT_EXCEPTION_VALUE_KEY); if (exceptionMessage != null) { retrievedCallback.onException(new ContextClientInvocationException(exceptionMessage)); } final CtxModelObjectBean modelObject; final Parcelable pModelObject = (Parcelable) intent.getParcelableExtra(ICtxClient.INTENT_RETURN_VALUE_KEY); if (pModelObject instanceof CtxModelObjectBean) { modelObject = (CtxModelObjectBean) pModelObject; } else { retrievedCallback.onException(new ContextClientInvocationException("Unexpected return value type: " + ((pModelObject != null) ? pModelObject.getClass() : "null"))); return; } retrievedCallback.onUpdateCtx(modelObject); } else { Log.e(LOG_TAG, "Could not find callback for received action " + intent.getAction()); } } else { Log.e(LOG_TAG, "Could not find callback method queue for received action " + intent.getAction()); } } } } /** * Create a broadcast receiver * * @return the created broadcast receiver */ private BroadcastReceiver setupBroadcastReceiver() { Log.d(LOG_TAG, "Set up broadcast receiver"); this.receiver = new ContextClientHelperReceiver(); this.context.registerReceiver(this.receiver, createIntentFilter()); Log.d(LOG_TAG, "Register broadcast receiver"); return receiver; } /** * Unregister the broadcast receiver */ private void teardownBroadcastReceiver() { Log.d(LOG_TAG, "Tear down broadcast receiver"); this.context.unregisterReceiver(this.receiver); } /** * Create a suitable intent filter * @return IntentFilter */ private IntentFilter createIntentFilter() { //register broadcast receiver to receive SocietiesEvents return values IntentFilter intentFilter = new IntentFilter(); Log.d(LOG_TAG, "intentFilter.addAction " + ICtxClient.CREATE_ASSOCIATION); intentFilter.addAction(ICtxClient.CREATE_ASSOCIATION); Log.d(LOG_TAG, "intentFilter.addAction " + ICtxClient.CREATE_ATTRIBUTE); intentFilter.addAction(ICtxClient.CREATE_ATTRIBUTE); Log.d(LOG_TAG, "intentFilter.addAction " + ICtxClient.CREATE_ENTITY); intentFilter.addAction(ICtxClient.CREATE_ENTITY); Log.d(LOG_TAG, "intentFilter.addAction " + ICtxClient.LOOKUP); intentFilter.addAction(ICtxClient.LOOKUP); Log.d(LOG_TAG, "intentFilter.addAction " + ICtxClient.RETRIEVE); intentFilter.addAction(ICtxClient.RETRIEVE); Log.d(LOG_TAG, "intentFilter.addAction " + ICtxClient.RETRIEVE_INDIVIDUAL_ENTITY_ID); intentFilter.addAction(ICtxClient.RETRIEVE_INDIVIDUAL_ENTITY_ID); Log.d(LOG_TAG, "intentFilter.addAction " + ICtxClient.UPDATE); intentFilter.addAction(ICtxClient.UPDATE); return intentFilter; } /** * Initialise a queue * * @param int index of class methods queue array */ private void initialiseQueue(int index) { if (null == this.methodQueues[index]) { Log.d(LOG_TAG, "Create queue for index: " + index); this.methodQueues[index] = new ConcurrentLinkedQueue<ICtxClientCallback>(); } } }