com.orangelabs.rcs.ri.ConnectionManager.java Source code

Java tutorial

Introduction

Here is the source code for com.orangelabs.rcs.ri.ConnectionManager.java

Source

/*******************************************************************************
 * Software Name : RCS IMS Stack
 *
 * Copyright (C) 2010 France Telecom S.A.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.orangelabs.rcs.ri;

import com.gsma.services.rcs.RcsService;
import com.gsma.services.rcs.RcsServiceControl;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.RcsServiceListener;
import com.gsma.services.rcs.RcsServiceListener.ReasonCode;
import com.gsma.services.rcs.RcsServiceRegistration;
import com.gsma.services.rcs.RcsServiceRegistrationListener;
import com.gsma.services.rcs.capability.CapabilityService;
import com.gsma.services.rcs.chat.ChatService;
import com.gsma.services.rcs.contact.ContactService;
import com.gsma.services.rcs.extension.MultimediaSessionService;
import com.gsma.services.rcs.filetransfer.FileTransferService;
import com.gsma.services.rcs.history.HistoryService;
import com.gsma.services.rcs.sharing.geoloc.GeolocSharingService;
import com.gsma.services.rcs.sharing.image.ImageSharingService;
import com.gsma.services.rcs.sharing.video.VideoSharingService;
import com.gsma.services.rcs.upload.FileUploadService;

import com.orangelabs.rcs.ri.settings.SettingsDisplay;
import com.orangelabs.rcs.ri.utils.LockAccess;
import com.orangelabs.rcs.ri.utils.LogUtils;
import com.orangelabs.rcs.ri.utils.TimerUtils;
import com.orangelabs.rcs.ri.utils.Utils;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * A class which manages connection to APIs and IMS platform
 * 
 * @author YPLO6403
 */
public class ConnectionManager {

    private final PendingIntent mPollCnxIntent;

    private static volatile ConnectionManager sInstance;

    /**
     * Notification ID
     */
    private final static int SERVICE_NOTIFICATION = 1000;

    private static final long API_CNX_POOLING_PERIOD = 30000;
    private static final long API_CNX_POOLING_START = 5000;

    /**
     * Enumerated type for RCS service name
     */
    @SuppressWarnings("javadoc")
    public enum RcsServiceName {
        CAPABILITY, CONTACT, CHAT, FILE_TRANSFER, IMAGE_SHARING, VIDEO_SHARING, GEOLOC_SHARING, FILE_UPLOAD, MULTIMEDIA, HISTORY;
    };

    /**
     * Set of connected services
     */
    final private Set<RcsServiceName> mConnectedServices;

    /**
     * Map of Activity / Client Connection notifier
     */
    final private Map<Activity, ClientConnectionNotifier> mClientsToNotify;

    /**
     * Map of RCS services and listeners
     */
    final private Map<RcsServiceName, RcsService> mApis;

    final private RcsServiceRegistrationListener mRcsServiceRegistrationListener = new RcsServiceRegistrationListener() {

        @Override
        public void onServiceUnregistered(RcsServiceRegistration.ReasonCode reason) {
            if (LogUtils.isActive) {
                Log.w(LOGTAG, "IMS Service Registration connection lost");
            }
            notifyImsDisconnection(reason);
        }

        @Override
        public void onServiceRegistered() {
            if (LogUtils.isActive) {
                Log.d(LOGTAG, "IMS Service Registered");
            }
            notifyImsConnection();
        }
    };

    private final Context mContext;

    private final Handler mHandler = new Handler();

    private RcsServiceControl mRcsServiceControl;

    /**
     * The set of managed services
     */
    private final Set<RcsServiceName> mManagedServices;

    /**
     * A service is elected to monitor the IMS registration events.
     */
    private final RcsServiceName mElectedService;

    private static final String LOGTAG = LogUtils.getTag(ConnectionManager.class.getSimpleName());

    private static final String ACTION_POOL_CONNECTION = "com.orangelabs.rcs.ri.ACTION_POOL_CONNECTION";

    /**
     * Client connection listener
     */
    private class ClientConnectionNotifier {
        /**
         * The set of monitored services
         */
        private final Set<RcsServiceName> mMonitoredServices;

        /**
         * The activity to notify
         */
        private Activity mActivity;

        /**
         * A locker to notify only once
         */
        private LockAccess mTriggerOnlyOnce;

        private RcsServiceListener mListener;

        /**
         * Constructor
         * 
         * @param activity the activity to notify
         * @param triggerOnlyOnce lock access to trigger only once
         * @param services the list of services to monitor
         */
        public ClientConnectionNotifier(Activity activity, LockAccess triggerOnlyOnce, RcsServiceName... services) {
            mActivity = activity;
            mTriggerOnlyOnce = triggerOnlyOnce;
            mMonitoredServices = new HashSet<RcsServiceName>();
            for (RcsServiceName service : services) {
                mMonitoredServices.add(service);
            }
        }

        /**
         * Constructor
         * 
         * @param listener Listener to execute if a connection event occurs
         * @param services services to notify
         */
        public ClientConnectionNotifier(RcsServiceListener listener, RcsServiceName... services) {
            mListener = listener;
            mMonitoredServices = new HashSet<RcsServiceName>();
            for (RcsServiceName service : services) {
                if (!mManagedServices.contains(service)) {
                    throw new IllegalArgumentException(
                            "Service " + service + " does not belong to set of managed services!");
                }
                mMonitoredServices.add(service);
            }
        }

        public void notifyConnection() {
            if (mListener == null) {
                return;
            }
            if (mConnectedServices.containsAll(mMonitoredServices)) {
                /* All monitored services are connected -> notify connection */
                mListener.onServiceConnected();
            }
        }

        public void notifyDisconnection(ReasonCode error) {
            if (mActivity != null) {
                String msg = null;
                if (ReasonCode.SERVICE_DISABLED == error) {
                    msg = mActivity.getString(R.string.label_api_disabled);
                } else {
                    msg = mActivity.getString(R.string.label_api_disconnected);
                }
                Utils.showMessageAndExit(mActivity, msg, mTriggerOnlyOnce);
                return;
            }
            if (mListener != null) {
                mListener.onServiceDisconnected(error);
            }
        }

        public Set<RcsServiceName> getMonitoredServices() {
            return mMonitoredServices;
        }

    }

    /**
     * A broadcast receiver to catch ACTION_SERVICE_UP from the RCS stack
     */
    private class RcsServiceReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (!RcsService.ACTION_SERVICE_UP.equals(intent.getAction())) {
                return;
            }
            if (LogUtils.isActive) {
                Log.i(LOGTAG, "RCS service is UP");
            }
            /* Connect all APIs */
            ConnectionManager.getInstance().connectApis();
        }
    }

    /**
     * Interface to listen for the response for RCS service starting state query
     */
    public interface IRcsServiceStartedListener {
        /**
         * Callback response
         * 
         * @param serviceStarted True if RCS services are started
         */
        public void handleResponse(boolean serviceStarted);
    }

    /**
     * Get an instance of ConnectionManager.
     * 
     * @param context the context
     * @param managedServices Set of managed services
     * @return the singleton instance.
     */
    public static ConnectionManager createInstance(Context context, Set<RcsServiceName> managedServices) {
        if (sInstance != null) {
            return sInstance;
        }
        synchronized (ConnectionManager.class) {
            if (sInstance == null) {
                if (context == null) {
                    throw new IllegalArgumentException("Context is null");
                }
                sInstance = new ConnectionManager(context, managedServices);
            }
        }
        return sInstance;
    }

    /**
     * Gets the singleton instance of ConnectionManager.
     * 
     * @return the singleton instance.
     */
    public static ConnectionManager getInstance() {
        return sInstance;
    }

    /**
     * Create a RCS service listener to monitor API connection
     * 
     * @param service the service to monitor
     * @return the listener
     */
    private RcsServiceListener newRcsServiceListener(final RcsServiceName service) {
        return new RcsServiceListener() {
            @Override
            public void onServiceDisconnected(ReasonCode error) {
                mConnectedServices.remove(service);
                notifyDisconnection(service, error);
                if (mElectedService == service) {
                    if (LogUtils.isActive) {
                        Log.w(LOGTAG, "API ".concat(error.name()));
                    }
                    notifyImsDisconnection(RcsServiceRegistration.ReasonCode.CONNECTION_LOST);
                }
            }

            @Override
            public void onServiceConnected() {
                mConnectedServices.add(service);
                notifyConnection(service);
                if (mElectedService != service) {
                    return;
                }
                /* Add a listener to be notified of IMS registration events */
                final RcsService service = mApis.get(mElectedService);
                mHandler.post(new Runnable() {
                    public void run() {
                        try {
                            service.addEventListener(mRcsServiceRegistrationListener);
                            if (service.isServiceRegistered()) {
                                notifyImsConnection();
                            } else {
                                RcsServiceRegistration.ReasonCode reason = service
                                        .getServiceRegistrationReasonCode();
                                notifyImsDisconnection(reason);
                            }
                        } catch (RcsServiceException e) {
                            if (LogUtils.isActive) {
                                Log.w(LOGTAG, "Cannot add RCS Service Registration Listener", e);
                            }
                        }
                    }
                });

            }
        };

    }

    /**
     * Constructor
     * 
     * @param context
     */
    private ConnectionManager(Context context, Set<RcsServiceName> managedServices) {
        mContext = context;
        mManagedServices = managedServices;
        mRcsServiceControl = RiApplication.getRcsServiceControl();
        /* Construct list of connected services */
        mConnectedServices = new HashSet<RcsServiceName>();
        /* Construct list of clients to notify */
        mClientsToNotify = new HashMap<Activity, ClientConnectionNotifier>();
        /* Construct list of APIs */
        mApis = new HashMap<RcsServiceName, RcsService>();

        mPollCnxIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_POOL_CONNECTION), 0);

        if (managedServices == null || managedServices.isEmpty()) {
            throw new RuntimeException("Incorrect parameter managedService!");
        }
        mElectedService = managedServices.iterator().next();
        /* Instantiate APIs */
        for (RcsServiceName service : mManagedServices) {
            switch (service) {
            case CAPABILITY:
                mApis.put(RcsServiceName.CAPABILITY,
                        new CapabilityService(context, newRcsServiceListener(RcsServiceName.CAPABILITY)));
                break;
            case CHAT:
                mApis.put(RcsServiceName.CHAT,
                        new ChatService(context, newRcsServiceListener(RcsServiceName.CHAT)));
                break;
            case CONTACT:
                mApis.put(RcsServiceName.CONTACT,
                        new ContactService(context, newRcsServiceListener(RcsServiceName.CONTACT)));
                break;
            case FILE_TRANSFER:
                mApis.put(RcsServiceName.FILE_TRANSFER,
                        new FileTransferService(context, newRcsServiceListener(RcsServiceName.FILE_TRANSFER)));
                break;
            case FILE_UPLOAD:
                mApis.put(RcsServiceName.FILE_UPLOAD,
                        new FileUploadService(context, newRcsServiceListener(RcsServiceName.FILE_UPLOAD)));
                break;
            case GEOLOC_SHARING:
                mApis.put(RcsServiceName.GEOLOC_SHARING,
                        new GeolocSharingService(context, newRcsServiceListener(RcsServiceName.GEOLOC_SHARING)));
                break;
            case HISTORY:
                mApis.put(RcsServiceName.HISTORY,
                        new HistoryService(context, newRcsServiceListener(RcsServiceName.HISTORY)));
                break;
            case IMAGE_SHARING:
                mApis.put(RcsServiceName.IMAGE_SHARING,
                        new ImageSharingService(context, newRcsServiceListener(RcsServiceName.IMAGE_SHARING)));
                break;
            case MULTIMEDIA:
                mApis.put(RcsServiceName.MULTIMEDIA,
                        new MultimediaSessionService(context, newRcsServiceListener(RcsServiceName.MULTIMEDIA)));
                break;
            case VIDEO_SHARING:
                mApis.put(RcsServiceName.VIDEO_SHARING,
                        new VideoSharingService(context, newRcsServiceListener(RcsServiceName.VIDEO_SHARING)));
                break;
            }
        }

    }

    private boolean isRcsServiceStarted() {
        try {
            return mRcsServiceControl.isServiceStarted();
        } catch (Exception e) {
            if (LogUtils.isActive) {
                Log.d(LOGTAG, "Failed to get RCS service starting state!", e);
            }
            return false;
        }
    }

    /**
     * Start connection manager
     */
    public void start() {
        /* Register the broadcast receiver to catch ACTION_SERVICE_UP */
        mContext.registerReceiver(new RcsServiceReceiver(), new IntentFilter(RcsService.ACTION_SERVICE_UP));

        /* Register the broadcast receiver to pool periodically the API connections */
        mContext.registerReceiver(new TimerPoolRcsServiceStartingState(), new IntentFilter(ACTION_POOL_CONNECTION));

        /* By default we consider that IMS connection is lost */
        notifyImsDisconnection(RcsServiceRegistration.ReasonCode.CONNECTION_LOST);

        /* Start pooling connection */
        AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        TimerUtils.setExactTimer(am, System.currentTimeMillis() + API_CNX_POOLING_START, mPollCnxIntent);

        connectApis();
    }

    /**
     * Connect APIs
     */
    private void connectApis() {
        if (isRcsServiceStarted()) {
            /* Connect all APIs */
            for (RcsServiceName service : mApis.keySet()) {
                /* Check if not already connected */
                if (!isServiceConnected(service)) {
                    try {
                        if (LogUtils.isActive) {
                            Log.d(LOGTAG, "Connect service ".concat(service.name()));
                        }
                        RcsService rcsService = mApis.get(service);
                        rcsService.connect();
                    } catch (Exception e) {
                        if (LogUtils.isActive) {
                            Log.e(LOGTAG, "Cannot connect service ".concat(service.name()), e);
                        }
                    }
                }
            }
        }
    }

    /**
     * Notify API disconnection to client
     * 
     * @param service the disconnected service
     * @param error the error
     */
    private void notifyDisconnection(RcsServiceName service, ReasonCode error) {
        if (LogUtils.isActive) {
            Log.w(LOGTAG, new StringBuilder(service.name()).append(" ").append(error).toString());
        }
        for (ClientConnectionNotifier clienttoNotify : mClientsToNotify.values()) {
            Set<RcsServiceName> monitoredServices = clienttoNotify.getMonitoredServices();
            if (monitoredServices == null || monitoredServices.contains(service)) {
                clienttoNotify.notifyDisconnection(error);
            }
        }
    }

    private void notifyConnection(RcsServiceName service) {
        if (LogUtils.isActive) {
            Log.w(LOGTAG, new StringBuilder(service.name()).append(" ").append(" is connected").toString());
        }
        for (ClientConnectionNotifier clienttoNotify : mClientsToNotify.values()) {
            clienttoNotify.notifyConnection();
        }
    }

    /**
     * Check if services are connected
     * 
     * @param services list of services
     * @return true if all services of the list are connected
     */
    public boolean isServiceConnected(RcsServiceName... services) {
        for (RcsServiceName service : services) {
            if (!mManagedServices.contains(service)) {
                throw new IllegalArgumentException(
                        "Service " + service + " does not belong to set of managed services!");
            }
            if (!mConnectedServices.contains(service)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Start monitoring the API connection by using a callback listener
     * 
     * @param activity the activity requesting to start monitoring the API connection
     * @param listener Listener to execute in case of connection event
     * @param services services to monitor
     */
    public void startMonitorApiCnx(Activity activity, RcsServiceListener listener, RcsServiceName... services) {
        mClientsToNotify.put(activity, new ClientConnectionNotifier(listener, services));
    }

    /**
     * Start monitoring the services and finish activities if service is disconnected
     * 
     * @param activity the activity requesting to start monitoring the services
     * @param exitOnce a locker
     * @param services the list of services to monitor
     */
    public void startMonitorServices(Activity activity, LockAccess exitOnce, RcsServiceName... services) {
        mClientsToNotify.put(activity, new ClientConnectionNotifier(activity, exitOnce, services));
    }

    /**
     * Stop monitoring the services
     * 
     * @param activity the activity requesting to stop monitoring the services
     */
    public void stopMonitorServices(Activity activity) {
        mClientsToNotify.remove(activity);
    }

    /**
     * Stop monitoring the API connection
     * 
     * @param activity the activity requesting to stop monitoring the API connection
     */
    public void stopMonitorApiCnx(Activity activity) {
        mClientsToNotify.remove(activity);
    }

    /**
     * Get the instance of CapabilityService
     * 
     * @return the instance
     */
    public CapabilityService getCapabilityApi() {
        return (CapabilityService) mApis.get(RcsServiceName.CAPABILITY);
    }

    /**
     * Get the instance of ChatService
     * 
     * @return the instance
     */
    public ChatService getChatApi() {
        return (ChatService) mApis.get(RcsServiceName.CHAT);
    }

    /**
     * Get the instance of ContactsService
     * 
     * @return the instance
     */
    public ContactService getContactApi() {
        return (ContactService) mApis.get(RcsServiceName.CONTACT);
    }

    /**
     * Get the instance of FileTransferService
     * 
     * @return the instance
     */
    public FileTransferService getFileTransferApi() {
        return (FileTransferService) mApis.get(RcsServiceName.FILE_TRANSFER);
    }

    /**
     * Get the instance of VideoSharingService
     * 
     * @return the instance
     */
    public VideoSharingService getVideoSharingApi() {
        return (VideoSharingService) mApis.get(RcsServiceName.VIDEO_SHARING);
    }

    /**
     * Get the instance of ImageSharingService
     * 
     * @return the instance
     */
    public ImageSharingService getImageSharingApi() {
        return (ImageSharingService) mApis.get(RcsServiceName.IMAGE_SHARING);
    }

    /**
     * Get the instance of GeolocSharingService
     * 
     * @return the instance
     */
    public GeolocSharingService getGeolocSharingApi() {
        return (GeolocSharingService) mApis.get(RcsServiceName.GEOLOC_SHARING);
    }

    /**
     * Get the instance of FileUploadService
     * 
     * @return the instance
     */
    public FileUploadService getFileUploadApi() {
        return (FileUploadService) mApis.get(RcsServiceName.FILE_UPLOAD);
    }

    /**
     * Get the instance of HistoryService
     * 
     * @return the instance
     */
    public HistoryService getHistoryApi() {
        return (HistoryService) mApis.get(RcsServiceName.HISTORY);
    }

    /**
     * Get the instance of MultimediaSessionService
     * 
     * @return the instance
     */
    public MultimediaSessionService getMultimediaSessionApi() {
        return (MultimediaSessionService) mApis.get(RcsServiceName.MULTIMEDIA);
    }

    private void notifyImsConnection() {
        addImsConnectionNotification(true, null);
    }

    private void notifyImsDisconnection(RcsServiceRegistration.ReasonCode reason) {
        addImsConnectionNotification(false, reason);
    }

    private void addImsConnectionNotification(boolean connected, RcsServiceRegistration.ReasonCode reason) {
        /* Create notification */
        Intent intent = new Intent(mContext, SettingsDisplay.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
        int iconId;
        String label;
        if (connected) {
            iconId = R.drawable.ri_notif_on_icon;
            label = mContext.getString(R.string.ims_connected);
        } else {
            iconId = R.drawable.ri_notif_off_icon;
            if (RcsServiceRegistration.ReasonCode.BATTERY_LOW == reason) {
                label = mContext.getString(R.string.ims_battery_disconnected);
            } else {
                label = mContext.getString(R.string.ims_disconnected);
            }
        }
        String title = mContext.getString(R.string.notification_title_rcs_service);
        /* Create notification */
        Notification notif = buildImsConnectionNotification(contentIntent, title, label, iconId);
        notif.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_FOREGROUND_SERVICE;
        /* Send notification */
        NotificationManager notificationManager = (NotificationManager) mContext
                .getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(SERVICE_NOTIFICATION, notif);
    }

    /**
     * Generate a notification
     * 
     * @param intent
     * @param title
     * @param message
     * @param iconId
     * @return the notification
     */
    private Notification buildImsConnectionNotification(PendingIntent intent, String title, String message,
            int iconId) {
        /* Create notification */
        NotificationCompat.Builder notif = new NotificationCompat.Builder(mContext);
        notif.setContentIntent(intent);
        notif.setSmallIcon(iconId);
        notif.setWhen(System.currentTimeMillis());
        notif.setAutoCancel(false);
        notif.setOnlyAlertOnce(true);
        notif.setContentTitle(title);
        notif.setContentText(message);
        return notif.build();
    }

    private class TimerPoolRcsServiceStartingState extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            new Thread() {
                public void run() {
                    try {
                        connectApis();
                    } catch (RuntimeException e) {
                        /*
                         * Intentionally catch runtime exceptions as else it will abruptly end the
                         * thread and eventually bring the whole system down, which is not intended.
                         */
                        Log.e(LOGTAG, "Failed to pool conection to RCS service!", e);
                    } finally {
                        AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
                        TimerUtils.setExactTimer(am, System.currentTimeMillis() + API_CNX_POOLING_PERIOD,
                                mPollCnxIntent);
                    }
                }
            }.start();
        }
    }

}