View Javadoc

1   /*
2    * Copyright (C) 2010 Teleal GmbH, Switzerland
3    *
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU Lesser General Public License as
6    * published by the Free Software Foundation, either version 3 of
7    * the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public License
15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16   */
17  
18  package org.teleal.cling.android;
19  
20  import android.net.wifi.WifiManager;
21  import org.teleal.cling.DefaultUpnpServiceConfiguration;
22  import org.teleal.cling.binding.xml.DeviceDescriptorBinder;
23  import org.teleal.cling.binding.xml.ServiceDescriptorBinder;
24  import org.teleal.cling.binding.xml.UDA10DeviceDescriptorBinderSAXImpl;
25  import org.teleal.cling.binding.xml.UDA10ServiceDescriptorBinderSAXImpl;
26  import org.teleal.cling.transport.impl.apache.StreamClientConfigurationImpl;
27  import org.teleal.cling.transport.impl.apache.StreamClientImpl;
28  import org.teleal.cling.transport.impl.apache.StreamServerConfigurationImpl;
29  import org.teleal.cling.transport.impl.apache.StreamServerImpl;
30  import org.teleal.cling.transport.spi.NetworkAddressFactory;
31  import org.teleal.cling.transport.spi.StreamClient;
32  import org.teleal.cling.transport.spi.StreamServer;
33  
34  import java.util.concurrent.ArrayBlockingQueue;
35  import java.util.concurrent.Executor;
36  import java.util.concurrent.ThreadPoolExecutor;
37  import java.util.concurrent.TimeUnit;
38  import java.util.logging.Logger;
39  
40  /**
41   * Configuration settings for deployment on Android.
42   * <p>
43   * This configuration utilizes the Apache HTTP Components transport implementation
44   * found in {@link org.teleal.cling.transport.impl.apache} for TCP/HTTP networking. It
45   * will attempt to bind only to the WiFi network interface and addresses on an
46   * Android device.
47   * </p>
48   * <p>
49   * This configuration utilizes the SAX default descriptor binders found in
50   * {@link org.teleal.cling.binding.xml}. The system property <code>org.xml.sax.driver</code>
51   * is set to <code>org.xmlpull.v1.sax2.Driver</code>.
52   * </p>
53   * <p>
54   * The thread <code>Executor</code> is a <code>ThreadPoolExecutor</code> with the following
55   * properties, optimized for machines with limited resources:
56   * </p>
57   * <ul>
58   * <li>Core pool size of minimum 8 idle threads</li>
59   * <li>Maximum 16 threads active</li>
60   * <li>5 seconds keep-alive time before an idle thread is removed from the pool</li>
61   * <li>A FIFO queue of maximum 512 tasks waiting for a thread from the pool</li>
62   * </ul>
63   * <p>
64   * A warning message will be logged when all threads of the pool have been exhausted
65   * and executions have to be dropped.
66   * </p>
67   *
68   * @author Christian Bauer
69   */
70  public class AndroidUpnpServiceConfiguration extends DefaultUpnpServiceConfiguration {
71  
72      final private static Logger log = Logger.getLogger(AndroidUpnpServiceConfiguration.class.getName());
73  
74      final protected WifiManager wifiManager;
75  
76      public AndroidUpnpServiceConfiguration(WifiManager wifiManager) {
77          this(wifiManager, 0); // Ephemeral port
78      }
79  
80      public AndroidUpnpServiceConfiguration(WifiManager wifiManager, int streamListenPort) {
81          super(streamListenPort, false);
82  
83          this.wifiManager = wifiManager;
84  
85          // This should be the default on Android 2.1 but it's not set by default
86          System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver");
87      }
88  
89      @Override
90      protected NetworkAddressFactory createNetworkAddressFactory(int streamListenPort) {
91          return new AndroidNetworkAddressFactory(wifiManager);
92      }
93  
94      @Override
95      public StreamServer createStreamServer(NetworkAddressFactory networkAddressFactory) {
96          return new StreamServerImpl(
97                  new StreamServerConfigurationImpl(
98                          networkAddressFactory.getStreamListenPort()
99                  )
100         );
101     }
102 
103     @Override
104     public StreamClient createStreamClient() {
105         return new StreamClientImpl(new StreamClientConfigurationImpl() {
106         	public int getConnectionTimeoutSeconds() {
107                 return 2;
108             }
109         	public int getDataReadTimeoutSeconds() {
110                 return 3;
111             }
112         	public boolean getStaleCheckingEnabled() {
113         		// comment from AndroidHttpClient.java:
114         		//
115                 // Turn off stale checking.  Our connections break all the time anyway,
116                 // and it's not worth it to pay the penalty of checking every time.
117         		return false;
118         	}
119         	public int getRequestRetryCount() {
120         		// since "connections break all the time anyway", limit number of retries to
121         		// minimize time spent in HttpClient.execute()
122         		return 1;
123         	}
124         });
125     }
126 
127     @Override
128     protected DeviceDescriptorBinder createDeviceDescriptorBinderUDA10() {
129         return new UDA10DeviceDescriptorBinderSAXImpl();
130     }
131 
132     @Override
133     protected ServiceDescriptorBinder createServiceDescriptorBinderUDA10() {
134         return new UDA10ServiceDescriptorBinderSAXImpl();
135     }
136 
137     @Override
138     public int getRegistryMaintenanceIntervalMillis() {
139         return 3000; // Preserve battery on Android, only run every 3 seconds
140     }
141 
142     @Override
143     protected Executor createDefaultExecutor() {
144 
145         // Smaller pool and larger queue on Android, devices do not have much resources...
146         ThreadPoolExecutor defaultExecutor = new ThreadPoolExecutor(8, 16, 5, TimeUnit.SECONDS, new ArrayBlockingQueue(512)) {
147             @Override
148             protected void beforeExecute(Thread thread, Runnable runnable) {
149                 super.beforeExecute(thread, runnable);
150                 thread.setName("Thread " + thread.getId() + " (Active: " + getActiveCount() + ")");
151             }
152         };
153 
154         defaultExecutor.setRejectedExecutionHandler(
155                 new ThreadPoolExecutor.DiscardPolicy() {
156                     @Override
157                     public void rejectedExecution(Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {
158 
159                         // Log and discard
160                         log.warning(
161                                 "Thread pool saturated, discarding execution " +
162                                 "of '"+runnable.getClass()+"', consider raising the " +
163                                 "maximum pool or queue size"
164                         );
165                         super.rejectedExecution(runnable, threadPoolExecutor);
166                     }
167                 }
168         );
169 
170         return defaultExecutor;
171     }
172 
173 }