de.yaacc.upnp.server.YaaccUpnpServerService.java Source code

Java tutorial

Introduction

Here is the source code for de.yaacc.upnp.server.YaaccUpnpServerService.java

Source

/*
 *
 * Copyright (C) 2013 www.yaacc.de 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package de.yaacc.upnp.server;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.http.ConnectionClosedException;
import org.apache.http.HttpException;
import org.apache.http.HttpServerConnection;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpService;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;

import org.fourthline.cling.binding.annotations.AnnotationLocalServiceBinder;
import org.fourthline.cling.model.DefaultServiceManager;
import org.fourthline.cling.model.ValidationError;
import org.fourthline.cling.model.ValidationException;
import org.fourthline.cling.model.meta.DeviceDetails;
import org.fourthline.cling.model.meta.DeviceIdentity;
import org.fourthline.cling.model.meta.Icon;
import org.fourthline.cling.model.meta.LocalDevice;
import org.fourthline.cling.model.meta.LocalService;
import org.fourthline.cling.model.meta.ManufacturerDetails;
import org.fourthline.cling.model.meta.ModelDetails;
import org.fourthline.cling.model.meta.UDAVersion;
import org.fourthline.cling.model.types.DLNACaps;
import org.fourthline.cling.model.types.DLNADoc;
import org.fourthline.cling.model.types.UDADeviceType;
import org.fourthline.cling.model.types.UDN;
import org.fourthline.cling.protocol.async.SendingNotificationAlive;
import org.fourthline.cling.support.avtransport.AbstractAVTransportService;
import org.fourthline.cling.support.connectionmanager.ConnectionManagerService;
import org.fourthline.cling.support.model.Protocol;
import org.fourthline.cling.support.model.ProtocolInfo;
import org.fourthline.cling.support.model.ProtocolInfos;
import org.fourthline.cling.support.renderingcontrol.AbstractAudioRenderingControl;
import org.fourthline.cling.support.xmicrosoft.AbstractMediaReceiverRegistrarService;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import de.yaacc.R;
import de.yaacc.upnp.UpnpClient;
import de.yaacc.upnp.server.avtransport.YaaccAVTransportService;
import de.yaacc.upnp.server.contentdirectory.YaaccContentDirectory;
import de.yaacc.util.NotificationId;

/**
 * A simple local upnp server implementation. This class encapsulate the creation
 * and registration of local upnp services. it is implemented as a android
 * service in order to run in background
 * 
 * @author Tobias Schoene (openbit)
 */
public class YaaccUpnpServerService extends Service {

    private static final String UDN_ID = "35" + // we make this look like a valid IMEI
            Build.BOARD.length() % 10 + Build.BRAND.length() % 10 + Build.CPU_ABI.length() % 10
            + Build.DEVICE.length() % 10 + Build.DISPLAY.length() % 10 + Build.HOST.length() % 10
            + Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 + Build.MODEL.length() % 10
            + Build.PRODUCT.length() % 10 + Build.TAGS.length() % 10 + Build.TYPE.length() % 10
            + Build.USER.length() % 10;

    public static int PORT = 4711;

    private LocalDevice localServer;
    private LocalDevice localRenderer;

    // make preferences available for the whole service, since there might be
    // more things to configure in the future
    SharedPreferences preferences;

    public static final String MEDIA_SERVER_UDN_ID = UDN_ID;

    public static final String MEDIA_RENDERER_UDN_ID = UDN_ID + "-1";

    private UpnpClient upnpClient;

    private boolean watchdog;

    private RequestListenerThread httpServer;

    private boolean initialized = false;

    /*
     * (non-Javadoc)
     * 
     * @see android.app.Service#onBind(android.content.Intent)
     */
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(this.getClass().getName(), "On Bind");
        // do nothing
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see android.app.Service#onStart(android.content.Intent, int)
     */
    @Override
    public void onStart(Intent intent, int startid) {

        // when the service starts, the preferences are initialized
        preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());

        if (getUpnpClient() == null) {
            setUpnpClient(new UpnpClient());
        }
        // the footprint of the onStart() method must be small
        // otherwise android will kill the service
        // in order of this circumstance we have to initialize the service
        // asynchronous
        Thread initializationThread = new Thread(new Runnable() {

            @Override
            public void run() {
                initialize();
            }
        });
        initializationThread.start();
        showNotification();
        Log.d(this.getClass().getName(), "End On Start");

    }

    @Override
    public void onDestroy() {
        Log.d(this.getClass().getName(), "Destroying the service");
        if (getUpnpClient() != null) {
            if (localServer != null) {
                getUpnpClient().localDeviceRemoved(getUpnpClient().getRegistry(), localServer);
                localServer = null;
            }
            if (localRenderer != null) {
                getUpnpClient().localDeviceRemoved(getUpnpClient().getRegistry(), localRenderer);
                localRenderer = null;
            }

        }
        if (httpServer != null) {
            try {
                httpServer.serversocket.close();
            } catch (IOException e) {
                Log.e(this.getClass().getName(), "Error while closing http request thread", e);
            }
        }
        cancleNotification();
        super.onDestroy();
    }

    /**
     * Displays the notification.
     */
    private void showNotification() {
        Intent notificationIntent = new Intent(this, YaaccUpnpServerControlActivity.class);
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this).setOngoing(true)
                .setSmallIcon(R.drawable.ic_launcher).setContentTitle("Yaacc Upnp Server")
                .setContentText(preferences
                        .getString(getApplicationContext().getString(R.string.settings_local_server_name_key), ""));
        mBuilder.setContentIntent(contentIntent);
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(
                Context.NOTIFICATION_SERVICE);
        // mId allows you to update the notification later on.
        mNotificationManager.notify(NotificationId.UPNP_SERVER.getId(), mBuilder.build());
    }

    /**
     * Cancels the notification.
     */
    private void cancleNotification() {
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(
                Context.NOTIFICATION_SERVICE);
        // mId allows you to update the notification later on.
        mNotificationManager.cancel(NotificationId.UPNP_SERVER.getId());

    }

    /**
     * 
     */
    private void initialize() {
        this.initialized = false;
        if (!getUpnpClient().isInitialized()) {
            getUpnpClient().initialize(getApplicationContext());
            watchdog = false;
            new Timer().schedule(new TimerTask() {

                @Override
                public void run() {
                    watchdog = true;
                }
            }, 30000L); // 30 sec. watchdog

            while (!getUpnpClient().isInitialized() && !watchdog) {
                // wait for upnpClient initialization
            }
        }
        if (getUpnpClient().isInitialized()) {
            if (preferences.getBoolean(
                    getApplicationContext().getString(R.string.settings_local_server_provider_chkbx), false)) {
                if (localServer == null) {
                    localServer = createMediaServerDevice();
                }
                getUpnpClient().getRegistry().addDevice(localServer);

                createHttpServer();
            }

            if (preferences.getBoolean(
                    getApplicationContext().getString(R.string.settings_local_server_receiver_chkbx), false)) {
                if (localRenderer == null) {
                    localRenderer = createMediaRendererDevice();
                }
                getUpnpClient().getRegistry().addDevice(localRenderer);
            }
            this.initialized = true;
        } else {
            throw new IllegalStateException("UpnpClient is not initialized!");
        }

        startUpnpAliveNotifications();
    }

    /**
     * creates a http request thread
     */
    private void createHttpServer() {
        // Create a HttpService for providing content in the network.
        try {

            httpServer = new RequestListenerThread(getApplicationContext());
            httpServer.start();

        } catch (BindException e) {
            Log.w(this.getClass().getName(), "Server already running");
        } catch (IOException e) {
            // FIXME Ignored right error handling on rebind needed
            Log.w(this.getClass().getName(), "ContentProvider can not be initialized!", e);
            // throw new
            // IllegalStateException("ContentProvider can not be initialized!",
            // e);
        }
    }

    /**
     * start sending periodical upnp alive notifications.
     */
    private void startUpnpAliveNotifications() {
        int upnpNotificationFrequency = getUpnpNotificationFrequency();
        if (upnpNotificationFrequency != -1
                && preferences.getBoolean(getString(R.string.settings_local_server_chkbx), false)) {
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    Log.d(YaaccUpnpServerService.this.getClass().getName(), "Sending upnp alive notivication");
                    SendingNotificationAlive sendingNotificationAlive = null;
                    if (localServer != null) {
                        sendingNotificationAlive = new SendingNotificationAlive(
                                getUpnpClient().getRegistry().getUpnpService(), localServer);
                        sendingNotificationAlive.run();
                    }
                    if (localRenderer != null) {
                        sendingNotificationAlive = new SendingNotificationAlive(
                                getUpnpClient().getRegistry().getUpnpService(), localRenderer);
                        sendingNotificationAlive.run();
                    }
                    startUpnpAliveNotifications();
                }
            }, upnpNotificationFrequency);

        }
    }

    /**
     * the time between two upnp alive notifications. -1 if never send a
     * notification
     * 
     * @return the time
     */
    private int getUpnpNotificationFrequency() {
        return Integer.parseInt(preferences.getString(
                getUpnpClient().getContext().getString(R.string.settings_sending_upnp_alive_interval_key), "5000"));
    }

    /**
     * Create a local upnp renderer device
     * 
     * @return the device
     */
    private LocalDevice createMediaRendererDevice() {
        LocalDevice device;
        String versionName;
        Log.d(this.getClass().getName(), "Create MediaRenderer with ID: " + MEDIA_SERVER_UDN_ID);
        try {
            versionName = getApplicationContext().getPackageManager()
                    .getPackageInfo(getApplicationContext().getPackageName(), 0).versionName;
        } catch (NameNotFoundException ex) {
            Log.e(this.getClass().getName(), "Error while creating device", ex);
            versionName = "??";
        }
        try {
            device = new LocalDevice(new DeviceIdentity(new UDN(MEDIA_RENDERER_UDN_ID)),
                    new UDADeviceType("MediaRenderer", 3),
                    // Used for shown name: first part of ManufactDet, first
                    // part of ModelDet and version number
                    new DeviceDetails("YAACC - MediaRenderer (" + getLocalServerName() + ")",
                            new ManufacturerDetails("yaacc", "http://www.yaacc.de"),
                            new ModelDetails(getLocalServerName() + "-Renderer",
                                    "Free Android UPnP AV MediaRender, GNU GPL", versionName),
                            new DLNADoc[] { new DLNADoc("DMS", DLNADoc.Version.V1_5),
                                    new DLNADoc("M-DMS", DLNADoc.Version.V1_5) },
                            new DLNACaps(new String[] { "av-upload", "image-upload", "audio-upload" })),
                    createDeviceIcons(), createMediaRendererServices(), null);

            return device;
        } catch (ValidationException e) {
            for (ValidationError validationError : e.getErrors()) {
                Log.d(getClass().getCanonicalName(), validationError.toString());
            }
            throw new IllegalStateException("Exception during device creation", e);
        }

    }

    /**
     * Create a local upnp renderer device
     * 
     * @return the device
     */
    private LocalDevice createMediaServerDevice() {
        // https://bitbucket.org/longkerdandy/chii2/src/
        LocalDevice device;
        String versionName;
        Log.d(this.getClass().getName(), "Create MediaServer whith ID: " + MEDIA_SERVER_UDN_ID);
        try {
            versionName = getApplicationContext().getPackageManager()
                    .getPackageInfo(getApplicationContext().getPackageName(), 0).versionName;
        } catch (NameNotFoundException ex) {
            Log.e(this.getClass().getName(), "Error while creating device", ex);
            versionName = "??";
        }
        try {

            // Yaacc Details
            // Used for shown name: first part of ManufactDet, first
            // part of ModelDet and version number
            DeviceDetails yaaccDetails = new DeviceDetails("YAACC - MediaServer(" + getLocalServerName() + ")",
                    new ManufacturerDetails("yaacc.de", "http://www.yaacc.de"),
                    new ModelDetails(getLocalServerName() + "-MediaServer",
                            "Free Android UPnP AV MediaServer, GNU GPL", versionName));

            DeviceIdentity identity = new DeviceIdentity(new UDN(MEDIA_SERVER_UDN_ID));

            device = new LocalDevice(identity, new UDADeviceType("MediaServer"), yaaccDetails, createDeviceIcons(),
                    createMediaServerServices());

            return device;
        } catch (ValidationException e) {
            Log.e(this.getClass().getName(), "Exception during device creation", e);
            Log.e(this.getClass().getName(), "Exception during device creation Errors:" + e.getErrors());
            throw new IllegalStateException("Exception during device creation", e);
        }

    }

    private Icon[] createDeviceIcons() {
        Drawable drawable = getResources().getDrawable(R.drawable.yaacc120_jpg);
        Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
        //
        // return new Icon("image/png", 48, 48, 24,
        // URI.create("/icon.png"),stream.toByteArray());
        ArrayList<Icon> icons = new ArrayList<Icon>();

        icons.add(
                new Icon("image/jpeg", 120, 120, 24, "yaacc120.jpg", getIconAsByteArray(R.drawable.yaacc120_jpg)));
        icons.add(new Icon("image/jpeg", 48, 48, 24, "yaacc48.jpg", getIconAsByteArray(R.drawable.yaacc48_jpg)));
        icons.add(new Icon("image/jpeg", 32, 32, 24, "yaacc32.jpg", getIconAsByteArray(R.drawable.yaacc32_jpg)));
        icons.add(new Icon("image/bmp", 120, 120, 24, "yaacc120_24.bmp",
                getIconAsByteArray(R.drawable.yaacc120_24_bmp)));
        icons.add(new Icon("image/png", 120, 120, 24, "yaacc120_24.png",
                getIconAsByteArray(R.drawable.yaacc120_24_png)));
        icons.add(new Icon("image/bmp", 120, 120, 8, "yaacc120_8.bmp",
                getIconAsByteArray(R.drawable.yaacc120_8_bmp)));
        icons.add(new Icon("image/png", 120, 120, 8, "yaacc120_8.png",
                getIconAsByteArray(R.drawable.yaacc120_8_png)));
        icons.add(
                new Icon("image/png", 48, 48, 24, "yaacc48_24.bmp", getIconAsByteArray(R.drawable.yaacc48_24_bmp)));
        icons.add(
                new Icon("image/png", 48, 48, 24, "yaacc48_24.png", getIconAsByteArray(R.drawable.yaacc48_24_png)));
        icons.add(new Icon("image/bmp", 48, 48, 8, "yaacc48_8.bmp", getIconAsByteArray(R.drawable.yaacc48_8_bmp)));
        icons.add(new Icon("image/png", 48, 48, 8, "yaacc48_8.png", getIconAsByteArray(R.drawable.yaacc48_8_png)));
        icons.add(
                new Icon("image/bmp", 32, 32, 24, "yaacc32_24.bmp", getIconAsByteArray(R.drawable.yaacc32_24_bmp)));
        icons.add(
                new Icon("image/png", 32, 32, 24, "yaacc32_24.png", getIconAsByteArray(R.drawable.yaacc32_24_png)));
        icons.add(new Icon("image/bmp", 32, 32, 8, "yaacc32_8.bmp", getIconAsByteArray(R.drawable.yaacc32_8_bmp)));
        icons.add(new Icon("image/png", 32, 32, 8, "yaacc32_8.png", getIconAsByteArray(R.drawable.yaacc32_8_png)));
        return icons.toArray(new Icon[icons.size()]);
    }

    private String getLocalServerName() {
        return preferences.getString(getApplicationContext().getString(R.string.settings_local_server_name_key),
                "Yaacc");
    }

    /**
     * Create the services provided by the server device
     * 
     * @return the services
     */
    private LocalService<?>[] createMediaServerServices() {
        List<LocalService<?>> services = new ArrayList<LocalService<?>>();
        services.add(createContentDirectoryService());
        services.add(createServerConnectionManagerService());
        services.add(createMediaReceiverRegistrarService());
        return services.toArray(new LocalService[] {});
    }

    /**
     * Create the renderer services provided by the device
     * 
     * @return the services
     */
    private LocalService<?>[] createMediaRendererServices() {
        List<LocalService<?>> services = new ArrayList<LocalService<?>>();
        services.add(createAVTransportService());
        services.add(createRendererConnectionManagerService());
        services.add(createRenderingControl());
        return services.toArray(new LocalService[] {});
    }

    /**
     * Creates an ContentDirectoryService. The content directory includes all
     * Files of the MediaStore.
     * 
     * @return The ContenDiractoryService.
     */
    @SuppressWarnings("unchecked")
    private LocalService<YaaccContentDirectory> createContentDirectoryService() {
        LocalService<YaaccContentDirectory> contentDirectoryService = new AnnotationLocalServiceBinder()
                .read(YaaccContentDirectory.class);
        contentDirectoryService
                .setManager(new DefaultServiceManager<YaaccContentDirectory>(contentDirectoryService, null) {

                    @Override
                    protected int getLockTimeoutMillis() {
                        return 1000;
                    }

                    @Override
                    protected YaaccContentDirectory createServiceInstance() throws Exception {
                        return new YaaccContentDirectory(getApplicationContext());
                    }
                });
        return contentDirectoryService;
    }

    /**
     * creates an AVTransportService
     * 
     * @return the service
     */
    @SuppressWarnings("unchecked")
    private LocalService<YaaccAVTransportService> createAVTransportService() {
        LocalService<YaaccAVTransportService> avTransportService = new AnnotationLocalServiceBinder()
                .read(YaaccAVTransportService.class);
        avTransportService.setManager(new DefaultServiceManager<YaaccAVTransportService>(avTransportService, null) {
            @Override
            protected int getLockTimeoutMillis() {
                return 1000;
            }

            @Override
            protected YaaccAVTransportService createServiceInstance() throws Exception {
                return new YaaccAVTransportService(getUpnpClient());
            }
        });
        return avTransportService;
    }

    private LocalService<AbstractAudioRenderingControl> createRenderingControl() {
        LocalService<AbstractAudioRenderingControl> renderingControlService = new AnnotationLocalServiceBinder()
                .read(AbstractAudioRenderingControl.class);
        renderingControlService.setManager(
                new DefaultServiceManager<AbstractAudioRenderingControl>(renderingControlService, null) {
                    @Override
                    protected int getLockTimeoutMillis() {
                        return 1000;
                    }

                    @Override
                    protected AbstractAudioRenderingControl createServiceInstance() throws Exception {
                        return new YaaccAudioRenderingControlService(getUpnpClient());
                    }
                });
        return renderingControlService;
    }

    private LocalService<AbstractMediaReceiverRegistrarService> createMediaReceiverRegistrarService() {
        LocalService<AbstractMediaReceiverRegistrarService> service = new AnnotationLocalServiceBinder()
                .read(AbstractMediaReceiverRegistrarService.class);
        service.setManager(new DefaultServiceManager<AbstractMediaReceiverRegistrarService>(service, null) {

            @Override
            protected int getLockTimeoutMillis() {
                return 1000;
            }

            @Override
            protected AbstractMediaReceiverRegistrarService createServiceInstance() throws Exception {
                return new YaaccMediaReceiverRegistrarService(getUpnpClient());
            }
        });
        return service;
    }

    /**
     * creates a ConnectionManagerService.
     * 
     * @return the service
     */
    @SuppressWarnings("unchecked")
    private LocalService<ConnectionManagerService> createServerConnectionManagerService() {
        LocalService<ConnectionManagerService> service = new AnnotationLocalServiceBinder()
                .read(ConnectionManagerService.class);
        final ProtocolInfos sourceProtocols = getSourceProtocolInfos();

        service.setManager(
                new DefaultServiceManager<ConnectionManagerService>(service, ConnectionManagerService.class) {

                    @Override
                    protected int getLockTimeoutMillis() {
                        return 1000;
                    }

                    @Override
                    protected ConnectionManagerService createServiceInstance() throws Exception {
                        return new ConnectionManagerService(sourceProtocols, null);
                    }
                });

        return service;
    }

    /**
     * creates a ConnectionManagerService.
     * 
     * @return the service
     */
    @SuppressWarnings("unchecked")
    private LocalService<ConnectionManagerService> createRendererConnectionManagerService() {
        LocalService<ConnectionManagerService> service = new AnnotationLocalServiceBinder()
                .read(ConnectionManagerService.class);
        final ProtocolInfos sinkProtocols = getSinkProtocolInfos();
        service.setManager(
                new DefaultServiceManager<ConnectionManagerService>(service, ConnectionManagerService.class) {

                    @Override
                    protected int getLockTimeoutMillis() {
                        return 1000;
                    }

                    @Override
                    protected ConnectionManagerService createServiceInstance() throws Exception {
                        return new ConnectionManagerService(null, sinkProtocols);
                    }
                });

        return service;
    }

    /**
     * @return
     */
    private ProtocolInfos getSourceProtocolInfos() {
        return new ProtocolInfos(new ProtocolInfo("http-get:*:audio:*"),
                new ProtocolInfo("http-get:*:audio/mpeg:*"), new ProtocolInfo("http-get:*:audio/x-mpegurl:*"),
                new ProtocolInfo("http-get:*:audio/x-wav:*"),
                new ProtocolInfo("http-get:*:audio/mpeg:DLNA.ORG_PN=MP3"),
                new ProtocolInfo("http-get:*:audio/mpeg:DLNA.ORG_PN=MP2"),
                new ProtocolInfo("http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE"),
                new ProtocolInfo("http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO"),
                new ProtocolInfo("http-get:*:audio/x-flac:*"), new ProtocolInfo("http-get:*:audio/x-aiff:*"),
                new ProtocolInfo("http-get:*:audio/x-ogg:*"), new ProtocolInfo("http-get:*:audio/wav:*"),
                new ProtocolInfo("http-get:*:audio/x-ape:*"), new ProtocolInfo("http-get:*:audio/x-m4a:*"),
                new ProtocolInfo("http-get:*:audio/x-m4b:*"), new ProtocolInfo("http-get:*:audio/x-wavpack:*"),
                new ProtocolInfo("http-get:*:audio/x-musepack:*"), new ProtocolInfo("http-get:*:audio/basic:*"),
                new ProtocolInfo("http-get:*:audio/L16;rate=11025;channels=2:DLNA.ORG_PN=LPCM"),
                new ProtocolInfo("http-get:*:audio/L16;rate=22050;channels=2:DLNA.ORG_PN=LPCM"),
                new ProtocolInfo("http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM"),
                new ProtocolInfo("http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM"),
                new ProtocolInfo("http-get:*:audio/L16;rate=88200;channels=2:DLNA.ORG_PN=LPCM"),
                new ProtocolInfo("http-get:*:audio/L16;rate=96000;channels=2:DLNA.ORG_PN=LPCM"),
                new ProtocolInfo("http-get:*:audio/L16;rate=192000;channels=2:DLNA.ORG_PN=LPCM"),
                new ProtocolInfo(Protocol.HTTP_GET, ProtocolInfo.WILDCARD, "audio/mpeg",
                        "DLNA.ORG_PN=MP3;DLNA.ORG_OP=01"),
                new ProtocolInfo("http-get:*:audio/mpeg:DLNA.ORG_PN=MP3"),
                new ProtocolInfo("http-get:*:audio/mpeg:DLNA.ORG_PN=MP3X"),
                new ProtocolInfo("http-get:*:audio/x-ms-wma:*"),
                new ProtocolInfo("http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE"),
                new ProtocolInfo("http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL"),
                new ProtocolInfo("http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAPRO"),
                new ProtocolInfo("http-get:*:image/gif:*"), new ProtocolInfo("http-get:*:image/jpeg:*"),
                new ProtocolInfo("http-get:*:image/png:*"), new ProtocolInfo("http-get:*:image/x-ico:*"),
                new ProtocolInfo("http-get:*:image/x-ms-bmp:*"),
                new ProtocolInfo("http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG"),
                new ProtocolInfo("http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED"),
                new ProtocolInfo("http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM"),
                new ProtocolInfo("http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN"),
                new ProtocolInfo("http-get:*:image/x-ycbcr-yuv420:*"), new ProtocolInfo("http-get:*:video/mp4:*"),
                new ProtocolInfo("http-get:*:video/mpeg:*"), new ProtocolInfo("http-get:*:video/quicktime:*"),
                new ProtocolInfo("http-get:*:video/x-flc:*"), new ProtocolInfo("http-get:*:video/x-msvideo:*"),
                new ProtocolInfo(Protocol.HTTP_GET, ProtocolInfo.WILDCARD, "video/mpeg",
                        "DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=01;DLNA.ORG_CI=0"),
                new ProtocolInfo("http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1"),
                new ProtocolInfo("http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC"),
                new ProtocolInfo("http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC_XAC3"),
                new ProtocolInfo("http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL"),
                new ProtocolInfo("http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL_XAC3"),
                new ProtocolInfo("http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_PAL"),
                new ProtocolInfo("http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_PAL_XAC3"),
                new ProtocolInfo("http-get:*:video/wtv:*"),
                new ProtocolInfo("http-get:*:video/x-ms-asf:DLNA.ORG_PN=MPEG4_P2_ASF_ASP_L4_SO_G726"),
                new ProtocolInfo("http-get:*:video/x-ms-asf:DLNA.ORG_PN=MPEG4_P2_ASF_ASP_L5_SO_G726"),
                new ProtocolInfo("http-get:*:video/x-ms-asf:DLNA.ORG_PN=MPEG4_P2_ASF_SP_G726"),
                new ProtocolInfo("http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA"),
                new ProtocolInfo("http-get:*:video/x-ms-wmv:*"),
                new ProtocolInfo("http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL"),
                new ProtocolInfo("http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO"),
                new ProtocolInfo("http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE"),
                new ProtocolInfo("http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL"),
                new ProtocolInfo("http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO"),
                new ProtocolInfo("http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPLL_BASE"),
                new ProtocolInfo("http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPML_BASE"),
                new ProtocolInfo("http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPML_MP3"));

    }

    private ProtocolInfos getSinkProtocolInfos() {
        return new ProtocolInfos(new ProtocolInfo("http-get:*:*:*"), new ProtocolInfo("http-get:*:audio/mkv:*"),
                new ProtocolInfo("http-get:*:audio/mpegurl:*"), new ProtocolInfo("http-get:*:audio/mpeg:*"),
                new ProtocolInfo("http-get:*:audio/mpeg3:*"), new ProtocolInfo("http-get:*:audio/mp3:*"),
                new ProtocolInfo("http-get:*:audio/mp4:*"), new ProtocolInfo("http-get:*:audio/basic:*"),
                new ProtocolInfo("http-get:*:audio/midi:*"), new ProtocolInfo("http-get:*:audio/ulaw:*"),
                new ProtocolInfo("http-get:*:audio/ogg:*"), new ProtocolInfo("http-get:*:audio/DVI4:*"),
                new ProtocolInfo("http-get:*:audio/G722:*"), new ProtocolInfo("http-get:*:audio/G723:*"),
                new ProtocolInfo("http-get:*:audio/G726-16:*"), new ProtocolInfo("http-get:*:audio/G726-24:*"),
                new ProtocolInfo("http-get:*:audio/G726-32:*"), new ProtocolInfo("http-get:*:audio/G726-40:*"),
                new ProtocolInfo("http-get:*:audio/G728:*"), new ProtocolInfo("http-get:*:audio/G729:*"),
                new ProtocolInfo("http-get:*:audio/G729D:*"), new ProtocolInfo("http-get:*:audio/G729E:*"),
                new ProtocolInfo("http-get:*:audio/GSM:*"), new ProtocolInfo("http-get:*:audio/GSM-EFR:*"),
                new ProtocolInfo("http-get:*:audio/L8:*"), new ProtocolInfo("http-get:*:audio/L16:*"),
                new ProtocolInfo("http-get:*:audio/LPC:*"), new ProtocolInfo("http-get:*:audio/MPA:*"),
                new ProtocolInfo("http-get:*:audio/PCMA:*"), new ProtocolInfo("http-get:*:audio/PCMU:*"),
                new ProtocolInfo("http-get:*:audio/QCELP:*"), new ProtocolInfo("http-get:*:audio/RED:*"),
                new ProtocolInfo("http-get:*:audio/VDVI:*"), new ProtocolInfo("http-get:*:audio/ac3:*"),
                new ProtocolInfo("http-get:*:audio/vorbis:*"), new ProtocolInfo("http-get:*:audio/speex:*"),
                new ProtocolInfo("http-get:*:audio/flac:*"), new ProtocolInfo("http-get:*:audio/x-flac:*"),
                new ProtocolInfo("http-get:*:audio/x-aiff:*"),
                new ProtocolInfo("http-get:*:audio/x-pn-realaudio:*"),
                new ProtocolInfo("http-get:*:audio/x-realaudio:*"), new ProtocolInfo("http-get:*:audio/x-wav:*"),
                new ProtocolInfo("http-get:*:audio/x-matroska:*"), new ProtocolInfo("http-get:*:audio/x-ms-wma:*"),
                new ProtocolInfo("http-get:*:audio/x-mpegurl:*"),
                new ProtocolInfo("http-get:*:application/x-shockwave-flash:*"),
                new ProtocolInfo("http-get:*:application/ogg:*"), new ProtocolInfo("http-get:*:application/sdp:*"),
                new ProtocolInfo("http-get:*:image/gif:*"), new ProtocolInfo("http-get:*:image/jpeg:*"),
                new ProtocolInfo("http-get:*:image/ief:*"), new ProtocolInfo("http-get:*:image/png:*"),
                new ProtocolInfo("http-get:*:image/tiff:*"), new ProtocolInfo("http-get:*:video/avi:*"),
                new ProtocolInfo("http-get:*:video/divx:*"), new ProtocolInfo("http-get:*:video/mpeg:*"),
                new ProtocolInfo("http-get:*:video/fli:*"), new ProtocolInfo("http-get:*:video/flv:*"),
                new ProtocolInfo("http-get:*:video/quicktime:*"), new ProtocolInfo("http-get:*:video/vnd.vivo:*"),
                new ProtocolInfo("http-get:*:video/vc1:*"), new ProtocolInfo("http-get:*:video/ogg:*"),
                new ProtocolInfo("http-get:*:video/mp4:*"), new ProtocolInfo("http-get:*:video/mkv:*"),
                new ProtocolInfo("http-get:*:video/BT656:*"), new ProtocolInfo("http-get:*:video/CelB:*"),
                new ProtocolInfo("http-get:*:video/JPEG:*"), new ProtocolInfo("http-get:*:video/H261:*"),
                new ProtocolInfo("http-get:*:video/H263:*"), new ProtocolInfo("http-get:*:video/H263-1998:*"),
                new ProtocolInfo("http-get:*:video/H263-2000:*"), new ProtocolInfo("http-get:*:video/MPV:*"),
                new ProtocolInfo("http-get:*:video/MP2T:*"), new ProtocolInfo("http-get:*:video/MP1S:*"),
                new ProtocolInfo("http-get:*:video/MP2P:*"), new ProtocolInfo("http-get:*:video/BMPEG:*"),
                new ProtocolInfo("http-get:*:video/xvid:*"), new ProtocolInfo("http-get:*:video/x-divx:*"),
                new ProtocolInfo("http-get:*:video/x-matroska:*"), new ProtocolInfo("http-get:*:video/x-ms-wmv:*"),
                new ProtocolInfo("http-get:*:video/x-ms-avi:*"), new ProtocolInfo("http-get:*:video/x-flv:*"),
                new ProtocolInfo("http-get:*:video/x-fli:*"), new ProtocolInfo("http-get:*:video/x-ms-asf:*"),
                new ProtocolInfo("http-get:*:video/x-ms-asx:*"), new ProtocolInfo("http-get:*:video/x-ms-wmx:*"),
                new ProtocolInfo("http-get:*:video/x-ms-wvx:*"), new ProtocolInfo("http-get:*:video/x-msvideo:*"),
                new ProtocolInfo("http-get:*:video/x-xvid:*"), new ProtocolInfo("http-get:*:audio/L16:*"),
                new ProtocolInfo("http-get:*:audio/mp3:*"), new ProtocolInfo("http-get:*:audio/x-mp3:*"),
                new ProtocolInfo("http-get:*:audio/mpeg:*"), new ProtocolInfo("http-get:*:audio/x-ms-wma:*"),
                new ProtocolInfo("http-get:*:audio/wma:*"), new ProtocolInfo("http-get:*:audio/mpeg3:*"),
                new ProtocolInfo("http-get:*:audio/wav:*"), new ProtocolInfo("http-get:*:audio/x-wav:*"),
                new ProtocolInfo("http-get:*:audio/ogg:*"), new ProtocolInfo("http-get:*:audio/x-ogg:*"),
                new ProtocolInfo("http-get:*:audio/musepack:*"), new ProtocolInfo("http-get:*:audio/x-musepack:*"),
                new ProtocolInfo("http-get:*:audio/flac:*"), new ProtocolInfo("http-get:*:audio/x-flac:*"),
                new ProtocolInfo("http-get:*:audio/mp4:*"), new ProtocolInfo("http-get:*:audio/m4a:*"),
                new ProtocolInfo("http-get:*:audio/aiff:*"), new ProtocolInfo("http-get:*:audio/x-aiff:*"),
                new ProtocolInfo("http-get:*:audio/basic:*"), new ProtocolInfo("http-get:*:audio/x-wavpack:*"),
                new ProtocolInfo("http-get:*:application/octet-stream:*"));
    }

    /**
     * 
     * Listener thread for http requests.
     * 
     */
    static class RequestListenerThread extends Thread {
        private ServerSocket serversocket;
        private BasicHttpParams params;
        private HttpService httpService;

        public RequestListenerThread(Context context) throws IOException, BindException {
            serversocket = new ServerSocket(PORT);
            params = new BasicHttpParams();
            params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
                    .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
                    .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
                    .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
                    .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1");

            // Set up the HTTP protocol processor
            BasicHttpProcessor httpProcessor = new BasicHttpProcessor();
            httpProcessor.addInterceptor(new ResponseDate());
            httpProcessor.addInterceptor(new ResponseServer());
            httpProcessor.addInterceptor(new ResponseContent());
            httpProcessor.addInterceptor(new ResponseConnControl());

            // Set up the HTTP service
            this.httpService = new YaaccHttpService(httpProcessor, new DefaultConnectionReuseStrategy(),
                    new DefaultHttpResponseFactory(), context);

        }

        @Override
        public void run() {
            Log.d(getClass().getName(), "Listening on port " + serversocket.getLocalPort());
            while (!Thread.interrupted()) {
                try {
                    // Set up HTTP connection
                    Socket socket = serversocket.accept();
                    DefaultHttpServerConnection connection = new DefaultHttpServerConnection();
                    Log.d(getClass().getName(), "Incoming connection from " + socket.getInetAddress());
                    connection.bind(socket, params);
                    // Start worker thread
                    Thread workerThread = new WorkerThread(httpService, connection);
                    workerThread.setDaemon(true);
                    workerThread.start();
                } catch (InterruptedIOException ex) {
                    break;
                } catch (IOException e) {
                    Log.d(getClass().getName(), "I/O error initialising connection thread: ", e);
                    break;
                }
            }
        }

    }

    static class WorkerThread extends Thread {

        private final HttpService httpservice;
        private final HttpServerConnection conn;

        public WorkerThread(final HttpService httpservice, final HttpServerConnection conn) {
            super();
            this.httpservice = httpservice;
            this.conn = conn;
        }

        @Override
        public void run() {
            Log.d(getClass().getName(), "New connection thread");
            try {
                Log.d(getClass().getName(), "conn.isOpen(): " + conn.isOpen());
                Log.d(getClass().getName(), "!Thread.interrupted(): " + !Thread.interrupted());
                while (!Thread.interrupted() && conn.isOpen()) {
                    conn.setSocketTimeout(500000); //not Timeout
                    HttpContext context = new BasicHttpContext();
                    httpservice.handleRequest(conn, context);
                }
            } catch (ConnectionClosedException ex) {
                Log.d(getClass().getName(), "Client closed connection", ex);
            } catch (IOException ex) {
                Log.d(getClass().getName(), "I/O error: ", ex);
            } catch (HttpException ex) {
                Log.d(getClass().getName(), "Unrecoverable HTTP protocol violation: ", ex);
            } finally {
                try {
                    Log.d(getClass().getName(), "Shutdown connection!");
                    conn.shutdown();
                } catch (IOException ignore) {
                    // ignore it
                    Log.d(getClass().getName(), "Error closing connection: ", ignore);
                }

            }
        }

    }

    // private boolean isYaaccUpnpServerServiceRunning() {
    // ActivityManager manager = (ActivityManager)
    // getSystemService(Context.ACTIVITY_SERVICE);
    // for (RunningServiceInfo service :
    // manager.getRunningServices(Integer.MAX_VALUE)) {
    // if (this.getClass().getName().equals(service.service.getClassName())) {
    // return true;
    // }
    // }
    // return false;
    // }

    private byte[] getIconAsByteArray(int drawableId) {

        Drawable drawable = getResources().getDrawable(drawableId);
        byte[] result = null;
        if (drawable != null) {
            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
            result = stream.toByteArray();
        }
        return result;
    }

    /**
     * @return the upnpClient
     */
    public UpnpClient getUpnpClient() {
        return upnpClient;
    }

    /**
     * @param upnpClient the upnpClient to set
     */
    private void setUpnpClient(UpnpClient upnpClient) {
        this.upnpClient = upnpClient;
    }

    /**
     * @return the initialized
     */
    public boolean isInitialized() {
        return initialized;
    }

}