content: /*
   * CDDL HEADER START
   *
   * The contents of this file are subject to the terms of the Common Development
   * and Distribution License (the "License").
   * You may not use this file except in compliance with the License.
   *
   * You can obtain a copy of the license at
   * src/com/vodafone360/people/VODAFONE.LICENSE.txt or
   * http://github.com/360/360-Engine-for-Android
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   * When distributing Covered Code, include this CDDL HEADER in each file and
   * include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt.
   * If applicable, add the following below this CDDL HEADER, with the fields
   * enclosed by brackets "[]" replaced with your own identifying information:
   * Portions Copyright [yyyy] [name of copyright owner]
   *
   * CDDL HEADER END
   *
   * Copyright 2010 Vodafone Sales & Services Ltd.  All rights reserved.
   * Use is subject to license terms.
   */
  
  package com.vodafone360.people.service.aidl;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Map;
  
  import android.app.Service;
  import android.content.Intent;
  import android.database.sqlite.SQLiteDatabase;
  import android.os.Bundle;
  import android.os.DeadObjectException;
  import android.os.Handler;
  import android.os.IBinder;
  import android.os.Message;
  import android.os.RemoteException;
  
  import com.vodafone360.people.MainApplication;
  import com.vodafone360.people.Settings;
  import com.vodafone360.people.SettingsManager;
  import com.vodafone360.people.datatypes.ContactSummary;
  import com.vodafone360.people.datatypes.Identity;
  import com.vodafone360.people.datatypes.LoginDetails;
  import com.vodafone360.people.datatypes.RegistrationDetails;
  import com.vodafone360.people.datatypes.ContactSummary.OnlineStatus;
  import com.vodafone360.people.engine.presence.PresenceDbUtils;
  import com.vodafone360.people.engine.presence.User;
  import com.vodafone360.people.engine.presence.NetworkPresence.SocialNetwork;
  import com.vodafone360.people.service.PersistSettings;
  import com.vodafone360.people.service.RemoteService;
  import com.vodafone360.people.service.PersistSettings.InternetAvail;
  import com.vodafone360.people.service.agent.NetworkAgentState;
  import com.vodafone360.people.service.interfaces.IPeopleService;
  import com.vodafone360.people.utils.LogUtils;
  
  /**
   * This is an experimental AIDL API, which is currently unsupported by Vodafone
   * and disabled by default.
   *
   * Service is used for all third party interaction with the 360 Engines.  Usage
   * is best explained with a code example, but the short story is that this
   * class (once bound) tries to provide all the methods available from the
   * IPeopleService.
   */
  public class IdlPeopleInterface extends Service {
  
      /** Reference to MainApplication. **/
      private MainApplication mApplication;
      /** Reference to IPeopleService. **/
      private IPeopleService mPeopleService;
      /**
       * Map of IDatabaseSubscriber listeners.  We store a list of listeners with
       * unique string identifiers, as there are problems removing these from the
       * list otherwise.
       **/
      private Map<String, IDatabaseSubscriber> mListeners;
      /**
       * Need a direct handle on the database for certain lookup functions.
       */
      private SQLiteDatabase mDatabase;
  
      @Override
      public final void onCreate() {
          super.onCreate();
          LogUtils.logI("IdlPeopleInterface.onCreate() "
                  + "Initialising IPC for com.vodafone360.people");
  
          if (!SettingsManager.getBooleanProperty(Settings.ENABLE_AIDL_KEY)) {
              LogUtils.logW("IdlPeopleInterface.onCreate() "
                      + "AIDL is disabled at built time");
              return;
          }
  
          /** Start the Service. **/
          startService(new Intent(this, RemoteService.class));
  
          mApplication = (MainApplication) getApplication();
          mPeopleService = mApplication.getServiceInterface();
          if (mPeopleService == null) {
              LogUtils.logV("IdlPeopleInterface.onCreate() "
                      + "Waiting for service to load");
              mApplication.registerServiceLoadHandler(mServiceLoadedHandler);
          }
  
          if (mListeners == null) {
              mListeners = new HashMap<String, IDatabaseSubscriber>();
          }
      }
  
  
      /**
       * Handler so we know when the IPeopleService has been started.
       */
      private Handler mServiceLoadedHandler = new Handler() {
          public void handleMessage(final Message msg) {
              super.handleMessage(msg);
              mApplication.unRegisterServiceLoadHandler();
              onServiceLoaded();
          }
      };
  
      /***
       * Initialise now that the service has finally loaded.
       */
      private void onServiceLoaded() {
          LogUtils.logV("IdlPeopleInterface.onServiceLoaded() Service loaded");
          if (!SettingsManager.getBooleanProperty(Settings.ENABLE_AIDL_KEY)) {
              LogUtils.logI("IdlPeopleInterface.onStart() "
                      + "AIDL disabled at build time");
              return;
          }
          mPeopleService = mApplication.getServiceInterface();
          mDatabase = mApplication.getDatabase().getReadableDatabase();
          onStartListenerActivity();
      }
  
      /***
       * Destroy the interface.
       *
       * TODO: Notify listeners that the interface has been destroyed.
       */
      @Override
      public final void onDestroy() {
          super.onDestroy();
  
          mPeopleService.unsubscribe(mHandler);
          mPeopleService.removeEventCallback(mHandler);
      }
  
      /***
       * Start the IdlPeopleInterface service.
       *
       * @param intent Starting intent.
       * @param startId Starting identifier.
       */
      @Override
      public final void onStart(final Intent intent, final int startId) {
          super.onStart(intent, startId);
  
          if (!SettingsManager.getBooleanProperty(Settings.ENABLE_AIDL_KEY)) {
              LogUtils.logI("IdlPeopleInterface.onStart() "
                      + "AIDL disabled at build time");
              return;
          }
  
          if (mPeopleService != null) {
              onStartListenerActivity();
          } else {
              LogUtils.logW("IdlPeopleInterface.onStart() "
                      + "People service should not be NULL, waiting for it to "
                      + "start");
              mApplication.registerServiceLoadHandler(mServiceLoadedHandler);
          }
      }
  
      /***
       * Subscribe listener activities, so we get notifications whenever
       * anything happens in the engine.
       */
      private void onStartListenerActivity() {
          mPeopleService.subscribe(mHandler, -1L, true);
          mPeopleService.addEventCallback(mHandler);
  
          LogUtils.logI("IdlPeopleInterface.startListenerActivity() Notifying "
                  + "listeners that the service is now ready to receive AIDL "
                  + "calls");
  
          for (Iterator<String> subscribersIterator
                  = mListeners.keySet().iterator();
          subscribersIterator.hasNext();) {
              final String listenerIdentifier = subscribersIterator.next();
              final IDatabaseSubscriber listener
              = mListeners.get(listenerIdentifier);
              try {
                  listener.onServiceReady();
              } catch (RemoteException e) {
                  LogUtils.logW("IdlPeopleInterface.startListenerActivity() "
                          + "Got a RemoteException trying to contact ["
                          + listenerIdentifier + "][" + listener
                          + "] Removing from list of listeners");
                  subscribersIterator.remove();
              }
          }
      }
  
      /**
       * Handler for incoming Engine messages.  All messages are immediately
       * passed on to listening third parties.
       */
      private Handler mHandler = new Handler() {
          /**
           *  We want to handle incoming events from the main engine code by
           *  passing them out to all 3rdParty listeners via AIDL.
           *
           *  Two things to note:
           *  <li> This method is Synchronized - there are potentially many messages
           *  coming in from different engines. At the moment, they come from a single
           *  thread, but for maintenance reasons this handling is thread-safe, so that
           *  if a threaded mechanism is implemented in future we shouldn't have
           *  problem here.
           *  <li> The code is actually very simple - it runs through the list of
           *  listeners and directly passes out the Message. This is possible since
           *  the Message class implements the Parcelable interface, and so is meant
           *  to be very happy to be passed around in the way. One problem with this
           *  process as it stands (and this accounts for the presence of the third
           *  catch clause below) is that somewhere we seem to be putting Non-Parcelable
           *  objects into these Messages - so while we are "guaranteed" the ability
           *  to pass Messages directly via AIDL by the Parcelable interface, this
           *  isn't always the case.
           *
           *  @param message - the message being sent to us by the engine to notify
           *  us of some event.
           */
          @Override
          public synchronized void handleMessage(final Message message) {
              super.handleMessage(message);
              LogUtils.logI("IdlPeopleInterface.handleMessage() Got a new "
                      + "service message, sending it to subscribers - Message["
                      + message.toString() + "]");
  
              /*
               * Also synchronise over the list of listeners, as the put/remove
               * code for this list can be called from any number of external
               * threads via AIDL.
               */
              synchronized (mListeners) {
                  for (Iterator<String> subscribersIterator = mListeners.keySet().iterator(); 
                  subscribersIterator.hasNext();) {
                      /**
                       * Can't use iterator shorthand, as we might need to remove
                       * things from the list.
                       */
                      final String listenerIdentifier = subscribersIterator.next();
                      final IDatabaseSubscriber listener = mListeners.get(listenerIdentifier);
  
  
                      LogUtils.logI("IdlPeopleInterface.handleMessage() "
                              + "Sending message to [" + listenerIdentifier + "]");
                      try {
                          //Pass the message out via IDL
                          listener.handleEvent(message);
  
                      } catch (DeadObjectException e) {
                          LogUtils.logE("IdlPeopleInterface.handleMessage() "
                                  + "DeadObjectException while trying to contact ["
                                  + listenerIdentifier + "][" + listener
                                  + "] - Removing from list of listeners", e);
                          subscribersIterator.remove();
                      } catch (RemoteException e){
                          LogUtils.logE("IdlPeopleInterface.handleMessage() "
                                  + "RemoteException while contacting listener", e);
                      } catch (NullPointerException e){
                          LogUtils.logE("IdlPeopleInterface.handleMessage() "
                                  + "NullPointerException while contacting" +
                                  " listenter. Possibly they have the wrong" +
                                  " dependencies.", e);
                      } catch (java.lang.RuntimeException e) {
                          /** Something in the message was not Parcelable. **/
                          if ("Can't marshal non-Parcelable objects across processes."
                                  .equals( e.getLocalizedMessage() )) {
                              /**
                               * Someone has packed something into the message
                               * that isn't Parcelable.
                               * TODO: Address why we're putting non-Parcelable
                               * objects into a Message (which is meant to
                               * implement the Parcelable class).
                               **/
                              LogUtils.logE("IdlPeopleInterface.handleMessage() "
                                      + "Tried to martial non-parcelable object", e);
                          } else {
                              /** Throw the Exception anyway. **/
                              LogUtils.logE("IdlPeopleInterface.handleMessage() "
                                      + "RuntimeException", e);
                              throw(e);
                          }
                      }
                  }
              }
          }
      };
  
      @Override
      public final boolean onUnbind(final Intent intent) {
          return super.onUnbind(intent);
      }
  
      /**
       * onBind is the function called when you try to get a handle on
       * and AIDL service.
       *
       * The documentation at:
       * http://developer.android.com/guide/topics/fundamentals.html
       *
       * recommends that onBind be implemented in a thread-safe manner,
       * so the method is synchronized.
       *
       * @param intent Calling Intent.
       */
      @Override
      public final synchronized IBinder onBind(final Intent intent) {
          LogUtils.logV("IdlPeopleInterface.onBind()");
  
          if (SettingsManager.getBooleanProperty(Settings.ENABLE_AIDL_KEY)) {
              LogUtils.logV("IdlPeopleInterface.onBind() "
                      + "AIDL interface enabled, so passing a "
                      + "IPeopleSubscriptionService back to binder");
              return new IPeopleSubscriptionService();
  
          } else {
              LogUtils.logV("IdlPeopleInterface.onBind() "
                      + "AIDL service disabled at build time");
              return null;
          }
      }
  
      /**
       * The idea is that what we're exposing via AIDL should be as close as
       * possible to the IPeopleService interface.  Clients should be able to
       * access the services as if they were internal.
       *
       * This is set this up to "implement" the IPeopleService - of course it
       * does this simply by forwarding requests to a IPeopleService service
       * which takes the action.  The reason for this model is that this service
       * must track any and all changes made to the original service, so that
       * clients can continue to integrate with the latest changes.
       * 
       * This class is defined in four sections:
       * <li> Functions currently implemented & available through AIDL.
       * <li> Functions implemented, but available in a slightly different
       *          form via AIDL.
       * <li> Functions not yet implemented.
       * <li> Functions that will not be implemented in AIDL.
       *
       * Except where otherwise noted, all methods perform exactly the same
       * operations as the equivalents in
       * com.vodafone360.people.interfaces.IPeopleService
       */
  
      public class IPeopleSubscriptionService extends
      IDatabaseSubscriptionService.Stub implements IPeopleService,
      ThirdPartyUtils {
  
          /**
           * Subscribe to find out about any events the 360 Services would like
           * the UI to know about (and thus we'd like the client to know about).
           * This provides a push notification system for clients to hear about
           * interesting things going on.
           *
           * Returns:
           * <li> true if the service is ready to receive requests,
           * <li> false otherwise. Subscribers will be notified when the service
           *  is ready with a call to their "onServiceReady()" function.
           *
           * @param identifier Unique subscriber ID.
           * @param subscriber Subscriber IDatabaseSubscriber interface.
           * @return TRUE if the PeopleService is already ready.
           */
          @Override
          public final boolean subscribe(final String identifier,
                  final IDatabaseSubscriber subscriber) {
              LogUtils.logV("IdlPeopleInterface.subscribe() "
                      + "Adding subscriber to get push notifications");
              
              if (identifier == null || subscriber == null) {
                  // Null is not a valid option for either argument
                  return false;
              }
  
              boolean boundAndReady = false;
  
              // In case anyone else is also adding/removing/iterating
              synchronized (mListeners) {
                  mListeners.put(identifier, subscriber);
              }
              if (mPeopleService == null) {
                  LogUtils.logW("IdlPeopleInterface.subscribe() "
                          + "PeopleService was NULL while while trying to add ["
                          + subscriber + "] to the list of subscribers.");
                  LogUtils.logW("IdlPeopleInterface.subscribe() Starting service"
                          + " - will call back the listener when ready.");
                  mApplication.registerServiceLoadHandler(mServiceLoadedHandler);
                  boundAndReady = false;
              } else {
                  LogUtils.logI("IdlPeopleInterface.subscribe() Service ready. "
                          + "It's safe for [" + identifier + "] to make calls.");
  
                  /**
                   * Subscribe every time because IPeopleService is currently
                   * only capable of handling one subscriber.
                   */
                  mPeopleService.subscribe(mHandler, -1L, true);
                  boundAndReady = true;
              }
  
              return boundAndReady;
  
          }
  
          /**
           * Always call this when you want to finish, otherwise we have a memory
           * leak (among other things).
           *
           * @param identifier Some unique identifier for your subscriber.
           * @return TRUE if unsubscribe was successful.  Only really returns
           *          FALSE if you couldn't be found in the list of subscribers.
           */
          @Override
          public final boolean unsubscribe(final String identifier) {
              boolean managedToUnsubscribe = false;
  
              /**
               * Code is synchronised in case anyone else is also adding,
               * removing or iterating.
               */
              synchronized (mListeners) {
                  if (mListeners.remove(identifier) != null) {
                      LogUtils.logI("IdlPeopleInterface.unsubscribe()"
                              + "Successfully unsubscribed");
                      managedToUnsubscribe = true;
                  } else {
                      LogUtils.logI("IdlPeopleInterface.unsubscribe() "
                              + "Got unsubscribe call, but did not find the "
                              + "identifier in the list");
                      managedToUnsubscribe = false;
                  }
              }
  
              return managedToUnsubscribe;
          }
  
          /**
           * TODO: Currently doing a NullPointer check. Need to file a bug report
           * about this;  occasionally getting NullPointer exceptions when this
           * is called, and it's not obvious why. Considering adding similar
           * checks elsewhere since exceptions aren't propagated via AIDL - they
           * just crash the service.
           */
          @Override
          public final void checkForUpdates() {
              LogUtils.logI("IdlPeopleInterface.checkForUpdates()");
              try {
                  mPeopleService.checkForUpdates();
              } catch (NullPointerException e) {
                  LogUtils.logE("IdlPeopleInterface.checkForUpdates()"
                          + "NullPointerException mPeopleService["
                          + mPeopleService + "]", e);
              }
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.deleteIdentity()
           */
          @Override
          public final void deleteIdentity(final String network,
                  final String identityId) {
              mPeopleService.deleteIdentity(network, identityId);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.downloadMeProfileFirstTime()
           */
          @Override
          public final void downloadMeProfileFirstTime() {
              mPeopleService.downloadMeProfileFirstTime();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.fetchPrivacyStatementy()
           */
          @Override
          public final void fetchPrivacyStatement() {
              mPeopleService.fetchPrivacyStatement();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.fetchTermsOfService()
           */
          @Override
          public final void fetchTermsOfService() {
              mPeopleService.fetchTermsOfService();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.fetchUsernameState()
           */
          @Override
          public final void fetchUsernameState(final String username) {
              mPeopleService.fetchUsernameState(username);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.getAvailableThirdPartyIdentities()
           */
          @Override
          public final ArrayList<Identity> getAvailableThirdPartyIdentities() {
              return mPeopleService.getAvailableThirdPartyIdentities();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.getLoginRequired()
           */
          @Override
          public final boolean getLoginRequired()  {
              return mPeopleService.getLoginRequired();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.getMoreTimelines()
           */
          @Override
          public final void getMoreTimelines() {
              mPeopleService.getMoreTimelines();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.getMy360AndThirdPartyChattableIdentities()
           */
          @Override
          public final ArrayList<Identity> getMy360AndThirdPartyChattableIdentities() {
              return mPeopleService.getMy360AndThirdPartyChattableIdentities();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.getMyThirdPartyIdentities()
           */
          @Override
          public final ArrayList<Identity> getMyThirdPartyIdentities() {
              return mPeopleService.getMyThirdPartyIdentities();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.getOlderStatuses()
           */
          @Override
          public final void getOlderStatuses() {
              mPeopleService.getOlderStatuses();
          }
          
          /**
           * @see com.vodafone360.people.database.tables
           */
          public int getPresence(final long localContactID){
              return com.vodafone360.people.database.tables.ContactSummaryTable.
                          getPresence(localContactID).ordinal();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.getPresenceList()
           */
          @Override
          public final void getPresenceList(final long contactId) {
              mPeopleService.getPresenceList(contactId);
  
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.getRoamingDeviceSetting()
           */
          @Override
          public final boolean getRoamingDeviceSetting() {
              return mPeopleService.getRoamingDeviceSetting();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.getRoamingNotificationType()
           */
          @Override
          public final int getRoamingNotificationType() {
              return mPeopleService.getRoamingNotificationType();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.getStatuses()
           */
          @Override
          public final void getStatuses() {
              mPeopleService.getStatuses();
          }
  
          /**
           * Return user presence status.
           * Extra method provide to allow easy grabbing of user presence info.
           *
           * @param localContactId ID for contact.
           * @return User presence.
           */
          public User getUserPresenceStatusByLocalContactId(long localContactId) {
              LogUtils.logV("IdlPeopleInterface.getUserPresenceStatusByLocalContactId() "
                      + "(localContactId: " + localContactId + ")");
              return PresenceDbUtils.getUserPresenceStatusByLocalContactId(
                      localContactId, mApplication.getDatabase());
          }
  
          /***
           * @param loginDetails for user
           * @see com.vodafone360.people.service.interfaces.logon()
           */
          @Override
          public final void logon(final LoginDetails loginDetails) {
              mPeopleService.logon(loginDetails);
          }
  
          /***
           * This is a workaround - The client must pass in the ordinal of the
           * appropriate ENUM from PersistSettings.InternetAvail().  However the
           * internal classes and AIDL don't work together correctly.
           */
          public void notifyDataSettingChanged(int internetAvail) {
              notifyDataSettingChanged(
                      PersistSettings.InternetAvail.values()[internetAvail]);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.pingUserActivity()
           */
          @Override
          public final void pingUserActivity() {
              mPeopleService.pingUserActivity();
          }
  
          /***
           * @see com.vodafone360.people.service.interfaces.register()
           */
          @Override
          public final void register(final RegistrationDetails details) {
              mPeopleService.register(details);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.sendMessage()
           */
          @Override
          public final void sendMessage(final long toLocalContactId,
                  final String body, final int socialNetworkId) {
              LogUtils.logI("IdlPeopleInterface.sendMessage()");
              try {
                  mPeopleService.sendMessage(toLocalContactId, body, socialNetworkId);
              } catch (ArrayIndexOutOfBoundsException e) {
                  /*
                   * If the user passes in an invalid socialNetworkId then it can
                   * cause this exception in 
                   * NetworkPresence.SocialNetwork.getPresenceValue(int)
                   * 
                   * (No stack trace as the user doesn't need to know about the
                   * internals in this instance).
                   */
                  LogUtils.logE("Tried to send message to invalid social network.");
              }
          }
  
          /***
           * This is a workaround - The client must pass in the ordinal of the
           * appropriate ENUM from ContactSummary.OnlineStatus().  However the
           * internal classes and AIDL don't work together correctly.
           */
          public void setAvailability(final int status) {
              setAvailability(ContactSummary.OnlineStatus.values()[status]);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.setAvailability()
           * Not yet supported, as function overloading is not supported in AIDL.
           */
          public void setAvailability(int network, int status) {
              setAvailability(SocialNetwork.values()[network],
                      OnlineStatus.values()[status]);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.setIdentityStatus()
           */
          @Override
          public final void setIdentityStatus(final String network,
                  final String identityId, final boolean identityStatus) {
              mPeopleService.setIdentityStatus(network, identityId,
                      identityStatus);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.setNewUpdateFrequency()
           */
          @Override
          public final void setNewUpdateFrequency() {
              mPeopleService.setNewUpdateFrequency();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.setShowRoamingNotificationAgain()
           */
          @Override
          public final void setShowRoamingNotificationAgain(
                  final boolean showAgain) {
              mPeopleService.setShowRoamingNotificationAgain(showAgain);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.startBackgroundContactSync()
           */
          @Override
          public final void startBackgroundContactSync(final long delay) {
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.startContactSync()
           */
          @Override
          public final void startContactSync() {
              mPeopleService.startContactSync();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.startStatusesSync()
           */
          @Override
          public final void startStatusesSync() {
              mPeopleService.startStatusesSync();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.updateChatNotification()
           */
          @Override
          public final void updateChatNotification(final long localContactId) {
              mPeopleService.updateChatNotification(localContactId);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.uploadMeProfile()
           */
          @Override
          public final void uploadMeProfile() {
              mPeopleService.uploadMeProfile();
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.uploadMyStatus()
           */
          @Override
          public final void uploadMyStatus(final String statusText) {
              mPeopleService.uploadMyStatus(statusText);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.validateIdentityCredentials()
           */
          @Override
          public final void validateIdentityCredentials(final boolean dryRun,
                  final String network, final String username,
                  final String password, final Bundle identityCapabilityStatus) {
              mPeopleService.validateIdentityCredentials(dryRun, network, username,
                      password, identityCapabilityStatus);
          }
  
          /*
           * These are methods that we'd like to expose, but are difficult
           * through AIDL (see "substitute" versions above).  The main problem is
           * internal classes which require further investigation.
           */
          @Override
          public final void notifyDataSettingChanged(
                  final InternetAvail internetAvail) {
              mPeopleService.notifyDataSettingChanged(internetAvail);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.setAvailability()
           */
          @Override
          public final void setAvailability(final OnlineStatus status) {
              mPeopleService.setAvailability(status);
          }
  
          /**
           * @see com.vodafone360.people.service.interfaces.setAvailability()
           * Not currently working as function overloading is not supported in
           * AIDL.
           */
          public final void setAvailability(final SocialNetwork network,
                  final OnlineStatus  status) {
              mPeopleService.setAvailability(network, status);
          }
  
          /*
           * Below are the methods I've not yet exposed - mostly because either
           * they have non-primitive return types, or non-primitive arguments.
           * Since most of the data types used appear to be parcelable, I'll be
           * moving forward on this hopefully soon.
           *
           * An outstanding issue is that some classes implement the "Parcelable"
           * interface, but fail to provide a "CREATOR" field - which is
           * necessary for them to be dealt with properly in the AIDL.  This
           * functionality could be added to get these methods working.
           */
  
          /***
           * NetworkAgentState does not implement the Parcelable interface.
           * Unless we change this, there is no chance for this function to be
           * exposed via AIDL in its current form.
           */
          @Override
          public final NetworkAgentState getNetworkAgentState() {
              // Not exposed via AIDL.
              return null;
          }
  
  
          /***
           * NetworkAgentState does not implement the Parcelable interface.
           * Unless we change this, there is no chance for this function to be
           * exposed via AIDL in its current form.
           */
          @Override
          public void setNetworkAgentState(final NetworkAgentState state) {
              // Not exposed via AIDL.
          }
  
          /***
           * Because of the different mechanism for subscription/unsubscription
           * implemented by this service, these methods will remain unimplemented.
           * (When a client "subscribes" they get *all* notifications).
           */
          @Override
          public void addEventCallback(final Handler uiHandler) {
              // Not exposed via AIDL.
          }
  
          @Override
          public void removeEventCallback(final Handler uiHandler) {
              // Not exposed via AIDL.
          }
  
          @Override
          public void subscribe(final Handler handler, final Long contactId,
                  final boolean chat) {
              // Not exposed via AIDL.
          }
  
          @Override
          public void unsubscribe(final Handler handler) {
              // Not exposed via AIDL.
          }
  
          @Override
          public boolean isSettingStatusOnNetworkInProgress(String network) {
              return mPeopleService.isSettingStatusOnNetworkInProgress(network);
          }
      }
  }
  