控制
在扫描阶段,client获取到了UPnP设备的设备描述文档和服务描述文档,这样就可以用于控制媒体数据的推送和回调接收,本质上是服务动作请求,HTTP的请求方式为POST;
我们以推送媒体uri为例,分析cling的实现逻辑,在client端的使用方式:
public void setAvURL() {
try {
Service localService = this.executeDeviceItem.getDevice()
.findService(new UDAServiceType("AVTransport"));
if (localService != null) {
Log.e("set url", "set url" + this.uriString);
this.upnpService.getControlPoint().execute(
new SetAVTransportURIActionCallback(localService,
this.uriString, this.metaData, mHandle,
this.controlType));
} else {
Log.e("null", "null");
}
} catch (Exception localException) {
localException.printStackTrace();
}
}
在client端,控制设备步骤分为3步:
- 获取tv设备控制服务:通过选中的设备执行device.findService(serviceType);
- 获取控制点:通过执行UpnpService.getControlPoint()
- 执行指定控制命令:通过执行ControlPoint.execute(Runnable)
在cling的support库中,有一个目录为callback,路径为org.fourthline.cling.support.avtransport.callback,在该目录中,定义了多个Callback:
其中定义了SetAVTransportURI、GetMediaInfo、Pause、Play等;
首先我们可以按照client和server分为两个模块;
client指令发送
创建Runnable
在上面的逻辑中,执行了execute()方法,其中传入的参数类型为Runnable,说明SetAVTransportURI的类型为Runnable,我们逐层分析每一层的实现逻辑;
public abstract class SetAVTransportURI extends ActionCallback {
private static Logger log = Logger.getLogger(SetAVTransportURI.class.getName());
public SetAVTransportURI(Service service, String uri) {
this(new UnsignedIntegerFourBytes(0), service, uri, null);
}
public SetAVTransportURI(Service service, String uri, String metadata) {
this(new UnsignedIntegerFourBytes(0), service, uri, metadata);
}
public SetAVTransportURI(UnsignedIntegerFourBytes instanceId, Service service, String uri) {
this(instanceId, service, uri, null);
}
public SetAVTransportURI(UnsignedIntegerFourBytes instanceId, Service service, String uri, String metadata) {
super(new ActionInvocation(service.getAction("SetAVTransportURI")));
log.fine("Creating SetAVTransportURI action for URI: " + uri);
getActionInvocation().setInput("InstanceID", instanceId);
getActionInvocation().setInput("CurrentURI", uri);
getActionInvocation().setInput("CurrentURIMetaData", metadata);
}
@Override
public void success(ActionInvocation invocation) {
log.fine("Execution successful");
}
}
SetAVTransportURI继承自ActionCallback,ActionCallback实现了Runnable;
SetAVTransportURI构造方法中定义了UPnP设备instanceId、service(UPnP设备中包含的远程服务)、uri以及metadata;
在该callback中,定义了SetAVTransportURI()方法,该方法中执行了super()方法,其中传入了ActionInvocation对象:
protected ActionCallback(ActionInvocation actionInvocation) {
this.actionInvocation = actionInvocation;
}
public ActionInvocation getActionInvocation() {
return actionInvocation;
}
然后在SetAVTransportURI()方法中调用了getActionInvocation()方法获取了刚刚创建好的ActionInvocation对象,在构造方法中传入了service.getAction("SetAVTransportURI"),首先先看一下ActionInvocation类定义:
public class ActionInvocation<S extends Service> {
final protected Action<S> action;
final protected ClientInfo clientInfo;
protected Map<String, ActionArgumentValue<S>> input = new LinkedHashMap();
protected Map<String, ActionArgumentValue<S>> output = new LinkedHashMap();
………………
public ActionInvocation(Action<S> action) {
this(action, null, null, null);
}
……………………
public ActionInvocation(Action<S> action,
ActionArgumentValue<S>[] input,
ActionArgumentValue<S>[] output,
ClientInfo clientInfo) {
if (action == null) {
throw new IllegalArgumentException("Action can not be null");
}
this.action = action;
setInput(input);
setOutput(output);
this.clientInfo = clientInfo;
}
}
在该类中定义了两个Map,一个表示client端发送的action的指令集合,一个是server端发送到client的指令集合,目前我们只关注client端发送action的指令集合input;
同时将传入构造方法的service.getAction("SetAVTransportURI")值赋给了action,标明service.getAction("SetAVTransportURI")的类型为Action,我们看一下service.getAction("SetAVTransportURI"):
public Action<S> getAction(String name) {
return actions == null ? null : actions.get(name);
}
该方法中,通过传入的action的name获取从扫描阶段获取到的service中所支持的action指令,现在我们想获取的是SetAVTransportURI指令;
然后在SetAVTransportURI()方法中通过getActionInvocation()方法获取了刚刚创建好的ActionInvocation对象,然后调用了setInput()方法:
public void setInput(String argumentName, Object value) throws InvalidValueException {
setInput(new ActionArgumentValue(getInputArgument(argumentName), value));
}
public void setInput(ActionArgumentValue<S> value) {
input.put(value.getArgument().getName(), value);
}
将InstanceID、CurrentURI以及CurrentURIMetaData这3个key和它们对应的value值按照key-value的形式传入到ActionInvocation对象中,然后再调用了setInput重载的方法,其中将key-value值封装成了ActionArgumentValue对象,然后保存到LinkedHashMap对象input中;
我们看一下ActionArgumentValue对象是什么;
public class ActionArgumentValue<S extends Service> extends VariableValue {
final private ActionArgument<S> argument;
public ActionArgumentValue(ActionArgument<S> argument, Object value) throws InvalidValueException {
super(argument.getDatatype(), value != null && value.getClass().isEnum() ? value.toString() : value);
this.argument = argument;
}
public ActionArgument<S> getArgument() {
return argument;
}
}
该类表示动作输入或输出参数的值;
在new ActionArgumentValue对象的时候,其实还调用了getInputArgument()方法,传入了key值,得到一个ActionArgument类型的对象,该类主要用于描述单个操作参数(输入或输出) 。
所以我们看一下getInputArgument()方法逻辑:
protected ActionArgument<S> getInputArgument(String name) {
ActionArgument<S> argument = getAction().getInputArgument(name);
if (argument == null) throw new IllegalArgumentException("Argument not found: " + name);
return argument;
}
其中调用了getAction().getInputArgument(name),其中getAction()方法为:
public Action<S> getAction() {
return action;
}
其中action变量在ActionInvocation中定义的,其实上述刚刚通过service.getAction("name")方式获取到的Action对象,然后通过getAction().getInputArgument(name)获取到一个ActionArgument对象:
public ActionArgument<S> getInputArgument(String name) {
for (ActionArgument<S> arg : getInputArguments()) {
if (arg.isNameOrAlias(name)) return arg;
}
return null;
}
public ActionArgument<S>[] getInputArguments() {
return inputArguments;
}
在getInputArgument(String name)方法中,调用了getInputArguments()方法,其中getInputArguments()方法返回了inputArguments变量,该变量在Action类的构造方法中赋值的;
通过层层追溯,可知,Action的初始化在创建AVTransport Service和RenderingControl Service的使用,执行read()方法的时候,就创建好了对应的Action,即对应Service所支持的Action指令的集合;
即该方法就是从Action的inputArguments集合中获取到指定的操作参数,例如InstanceID、CurrentURI或者是CurrentURIMetaData;
最后在setInput()方法中,传入了ActionArgument对象,保存执行的指令操作;
执行Runnable
SetAVTransportURI创建成功之后,调用execute()方法就会执行SetAVTransportURI中的run()方法,我们追溯一下run()的实现逻辑,run()方法定义在ActionCallback中:
public void run() {
Service service = actionInvocation.getAction().getService();
// Local execution
if (service instanceof LocalService) {
LocalService localService = (LocalService)service;
// Executor validates input inside the execute() call immediately
localService.getExecutor(actionInvocation.getAction()).execute(actionInvocation);
if (actionInvocation.getFailure() != null) {
failure(actionInvocation, null);
} else {
success(actionInvocation);
}
// Remote execution
} else if (service instanceof RemoteService){
if (getControlPoint() == null) {
throw new IllegalStateException("Callback must be executed through ControlPoint");
}
RemoteService remoteService = (RemoteService)service;
// Figure out the remote URL where we'd like to send the action request to
URL controLURL = remoteService.getDevice().normalizeURI(remoteService.getControlURI());
// Do it
SendingAction prot = getControlPoint().getProtocolFactory().createSendingAction(actionInvocation, controLURL);
prot.run();
IncomingActionResponseMessage response = prot.getOutputMessage();
if (response == null) {
failure(actionInvocation, null);
} else if (response.getOperation().isFailed()) {
failure(actionInvocation, response.getOperation());
} else {
success(actionInvocation);
}
}
}
在run()方法中判断了service的类型,分为LocalService和RemoteService类型,因为UPnP设备中包含的service对于client端而言,是属于RemoteService,所以执行到RemoteService分支中:
SendingAction prot = getControlPoint().getProtocolFactory().createSendingAction(actionInvocation, controLURL);
prot.run();
在RemoteService分支中执行了上述逻辑,执行了createSendingAction()方法:
public SendingAction createSendingAction(ActionInvocation actionInvocation, URL controlURL) {
return new SendingAction(getUpnpService(), actionInvocation, controlURL);
}
在该方法中返回了new SendingAction对象,我们看一下SendingAction的构造方法:
public SendingAction(UpnpService upnpService, ActionInvocation actionInvocation, URL controlURL) {
super(upnpService, new OutgoingActionRequestMessage(actionInvocation, controlURL));
this.actionInvocation = actionInvocation;
}
SendingAction继承自SendingSync<IN extends StreamRequestMessage, OUT extends StreamResponseMessage>,SendingSync<IN extends StreamRequestMessage, OUT extends StreamResponseMessage>为抽象类,SendingSync<IN extends StreamRequestMessage, OUT extends StreamResponseMessage>又继承自SendingSync,调用了super()方法:
protected SendingSync(UpnpService upnpService, IN inputMessage) {
super(upnpService);
this.inputMessage = inputMessage;
}
至此,SendingAction对象就创建成功了;
最后调用了SendingAction对象的run()方法,run()方法在SendingSync类中定义:
public void run() {
try {
execute();
} catch (Exception ex) {
Throwable cause = Exceptions.unwrap(ex);
if (cause instanceof InterruptedException) {
log.log(Level.INFO, "Interrupted protocol '" + getClass().getSimpleName() + "': " + ex, cause);
} else {
throw new RuntimeException(
"Fatal error while executing protocol '" + getClass().getSimpleName() + "': " + ex, ex
);
}
}
}
在run()方法中执行了execute()方法,execute()方法为抽象方法,该方法的实现在子类SendingSync<IN extends StreamRequestMessage, OUT extends StreamResponseMessage>中:
final protected void execute() throws RouterException {
outputMessage = executeSync();
}
在该方法中调用了executeSync()方法,executeSync()方法也是抽象方法,该方法的实现在SendingAction中实现:
protected IncomingActionResponseMessage executeSync() throws RouterException {
return invokeRemote(getInputMessage());
}
其中调用了invokeRemote(getInputMessage())方法:
protected IncomingActionResponseMessage invokeRemote(OutgoingActionRequestMessage requestMessage) throws RouterException {
Device device = actionInvocation.getAction().getService().getDevice();
log.fine("Sending outgoing action call '" + actionInvocation.getAction().getName() + "' to remote service of: " + device);
IncomingActionResponseMessage responseMessage = null;
try {
StreamResponseMessage streamResponse = sendRemoteRequest(requestMessage);
……………………
return responseMessage;
} catch (ActionException ex) {
……………………
}
}
在该方法中执行了sendRemoteRequest()方法:
protected StreamResponseMessage sendRemoteRequest(OutgoingActionRequestMessage requestMessage)
throws ActionException, RouterException {
try {
log.fine("Writing SOAP request body of: " + requestMessage);
getUpnpService().getConfiguration().getSoapActionProcessor().writeBody(requestMessage, actionInvocation);
log.fine("Sending SOAP body of message as stream to remote device");
return getUpnpService().getRouter().send(requestMessage);
} catch (RouterException ex) {
Throwable cause = Exceptions.unwrap(ex);
if (cause instanceof InterruptedException) {
if (log.isLoggable(Level.FINE)) {
log.fine("Sending action request message was interrupted: " + cause);
}
throw new ActionCancelledException((InterruptedException)cause);
}
throw ex;
} catch (UnsupportedDataException ex) {
if (log.isLoggable(Level.FINE)) {
log.fine("Error writing SOAP body: " + ex);
log.log(Level.FINE, "Exception root cause: ", Exceptions.unwrap(ex));
}
throw new ActionException(ErrorCode.ACTION_FAILED, "Error writing request message. " + ex.getMessage());
}
}
client发送控制命令给server端的方法就是sendRemoteRequest()方法,在该方法中调用了writeBody()方法将控制命令写入和调用getUpnpService().getRouter().send(requestMessage)将控制命令发送出去;
writeBody
public void writeBody(ActionRequestMessage requestMessage, ActionInvocation actionInvocation) throws UnsupportedDataException {
log.fine("Writing body of " + requestMessage + " for: " + actionInvocation);
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
Document d = factory.newDocumentBuilder().newDocument();
Element body = writeBodyElement(d);
writeBodyRequest(d, body, requestMessage, actionInvocation);
if (log.isLoggable(Level.FINER)) {
log.finer("===================================== SOAP BODY BEGIN ============================================");
log.finer(requestMessage.getBodyString());
log.finer("-===================================== SOAP BODY END ============================================");
}
} catch (Exception ex) {
throw new UnsupportedDataException("Can't transform message payload: " + ex, ex);
}
}
该方法在SOAPActionProcessorImpl.java中实现的;
在该方法中构造XML请求格式数据,然后调用writeBodyRequest()方法将请求进行真正的封装;
send
public StreamResponseMessage send(StreamRequestMessage msg) throws RouterException {
lock(readLock);
try {
if (enabled) {
if (streamClient == null) {
log.fine("No StreamClient available, not sending: " + msg);
return null;
}
log.fine("Sending via TCP unicast stream: " + msg);
try {
return streamClient.sendRequest(msg);
} catch (InterruptedException ex) {
throw new RouterException("Sending stream request was interrupted", ex);
}
} else {
log.fine("Router disabled, not sending stream request: " + msg);
return null;
}
} finally {
unlock(readLock);
}
}
在上一逻辑中封装了请求message,这一步就是通过调用send()方法发送请求;
在该方法中调用了sendRequest()方法:
@Override
public StreamResponseMessage sendRequest(StreamRequestMessage requestMessage) {
final UpnpRequest requestOperation = requestMessage.getOperation();
log.fine("Preparing HTTP request message with method '" + requestOperation.getHttpMethodName() + "': " + requestMessage);
URL url = URIUtil.toURL(requestOperation.getURI());
HttpURLConnection urlConnection = null;
InputStream inputStream;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod(requestOperation.getHttpMethodName());
// Use the built-in expiration, we can't cancel HttpURLConnection
urlConnection.setReadTimeout(configuration.getTimeoutSeconds() * 1000);
urlConnection.setConnectTimeout(configuration.getTimeoutSeconds() * 1000);
applyRequestProperties(urlConnection, requestMessage);
applyRequestBody(urlConnection, requestMessage);
log.fine("Sending HTTP request: " + requestMessage);
inputStream = urlConnection.getInputStream();
return createResponse(urlConnection, inputStream);
} catch (ProtocolException ex) {
log.log(Level.WARNING, "HTTP request failed: " + requestMessage, Exceptions.unwrap(ex));
return null;
} catch (IOException ex) {
if (urlConnection == null) {
log.log(Level.WARNING, "HTTP request failed: " + requestMessage, Exceptions.unwrap(ex));
return null;
}
if (ex instanceof SocketTimeoutException) {
log.info(
"Timeout of " + getConfiguration().getTimeoutSeconds()
+ " seconds while waiting for HTTP request to complete, aborting: " + requestMessage
);
return null;
}
if (log.isLoggable(Level.FINE))
log.fine("Exception occurred, trying to read the error stream: " + Exceptions.unwrap(ex));
try {
inputStream = urlConnection.getErrorStream();
return createResponse(urlConnection, inputStream);
} catch (Exception errorEx) {
if (log.isLoggable(Level.FINE))
log.fine("Could not read error stream: " + errorEx);
return null;
}
} catch (Exception ex) {
log.log(Level.WARNING, "HTTP request failed: " + requestMessage, Exceptions.unwrap(ex));
return null;
} finally {
if (urlConnection != null) {
// Release any idle persistent connection, or "indicate that we don't want to use this server for a while"
urlConnection.disconnect();
}
}
}
该方法的实现在StreamClientImpl类中;
在该方法中就是真正的执行message的发送逻辑;
server接收指令
我们知道,在发送消息的时候,调用了StreamClient中的sendRequest()方法,StreamClient的创建逻辑在Router的enable()方法中初始化,同时在enable()方法中还为NetworkAddressFactory绑定了StreamServer,在UPnP设备端,会存在StreamServers,StreamServer用来接收HTTP请求并进行处理;
注册监听Router(预备)
所以我们看一下StreamServer如何接收处理HTTP请求;
首先还是通过RouterImpl.startAddressBasedTransports()方法入手:
protected void startAddressBasedTransports(Iterator<InetAddress> addresses) throws InitializationException {
while (addresses.hasNext()) {
InetAddress address = addresses.next();
// HTTP servers
StreamServer streamServer = getConfiguration().createStreamServer(networkAddressFactory);
if (streamServer == null) {
log.info("Configuration did not create a StreamServer for: " + address);
} else {
try {
if (log.isLoggable(Level.FINE))
log.fine("Init stream server on address: " + address);
streamServer.init(address, this);
streamServers.put(address, streamServer);
} catch (InitializationException ex) {
// Try to recover
Throwable cause = Exceptions.unwrap(ex);
if (cause instanceof BindException) {
log.warning("Failed to init StreamServer: " + cause);
if (log.isLoggable(Level.FINE))
log.log(Level.FINE, "Initialization exception root cause", cause);
log.warning("Removing unusable address: " + address);
addresses.remove();
continue; // Don't try anything else with this address
}
throw ex;
}
}
// DatagramIO处理逻辑
// Datagram I/O
DatagramIO datagramIO = getConfiguration().createDatagramIO(networkAddressFactory);
if (datagramIO == null) {
log.info("Configuration did not create a StreamServer for: " + address);
} else {
……………………
}
}
for (Map.Entry<InetAddress, StreamServer> entry : streamServers.entrySet()) {
if (log.isLoggable(Level.FINE))
log.fine("Starting stream server on address: " + entry.getKey());
getConfiguration().getStreamServerExecutorService().execute(entry.getValue());
}
for (Map.Entry<InetAddress, DatagramIO> entry : datagramIOs.entrySet()) {
……………………
}
}
我们主要关注StreamServer的处理逻辑:
StreamServer streamServer = getConfiguration().createStreamServer(networkAddressFactory);
if (streamServer == null) {
log.info("Configuration did not create a StreamServer for: " + address);
} else {
try {
if (log.isLoggable(Level.FINE))
log.fine("Init stream server on address: " + address);
streamServer.init(address, this);
streamServers.put(address, streamServer);
} catch (InitializationException ex) {
// Try to recover
Throwable cause = Exceptions.unwrap(ex);
if (cause instanceof BindException) {
log.warning("Failed to init StreamServer: " + cause);
if (log.isLoggable(Level.FINE))
log.log(Level.FINE, "Initialization exception root cause", cause);
log.warning("Removing unusable address: " + address);
addresses.remove();
continue; // Don't try anything else with this address
}
throw ex;
}
}
首先先执行了createStreamServer()方法,该方法在AndroidUpnpServiceConfiguration类中实现;
@Override
public StreamServer createStreamServer(NetworkAddressFactory networkAddressFactory) {
// Use Jetty, start/stop a new shared instance of JettyServletContainer
return new AsyncServletStreamServerImpl(
new AsyncServletStreamServerConfigurationImpl(
JettyServletContainer.INSTANCE,
networkAddressFactory.getStreamListenPort()
)
);
}
public class AsyncServletStreamServerImpl implements StreamServer<AsyncServletStreamServerConfigurationImpl> {
其中创建了AsyncServletStreamServerImpl对象,AsyncServletStreamServerImpl继承自StreamServer,所以StreamServer创建成功了;
然后调用streamServer.init()方法初始化好StreamServer对象,然后将初始化好的StreamServer对象添加到streamServers集合中;
我们看一下init()方法中执行了哪些逻辑:
synchronized public void init(InetAddress bindAddress, final Router router) throws InitializationException {
try {
if (log.isLoggable(Level.FINE))
log.fine("Setting executor service on servlet container adapter");
getConfiguration().getServletContainerAdapter().setExecutorService(
router.getConfiguration().getStreamServerExecutorService()
);
if (log.isLoggable(Level.FINE))
log.fine("Adding connector: " + bindAddress + ":" + getConfiguration().getListenPort());
hostAddress = bindAddress.getHostAddress();
localPort = getConfiguration().getServletContainerAdapter().addConnector(
hostAddress,
getConfiguration().getListenPort()
);
String contextPath = router.getConfiguration().getNamespace().getBasePath().getPath();
getConfiguration().getServletContainerAdapter().registerServlet(contextPath, createServlet(router));
} catch (Exception ex) {
throw new InitializationException("Could not initialize " + getClass().getSimpleName() + ": " + ex.toString(), ex);
}
}
在该方法中主要执行了3个逻辑:
- 为ServletContainerAdapter设置ExecutorService,ExecutorService为执行服务器,用于执行Runnable;
- 为ServletContainerAdapter添加Connector,用于连接访问执行的ip地址和端口号;
- 为ServletContainerAdapter注册Servlet,将需要监听的router注册进来;
其中最终的为第3步:
String contextPath = router.getConfiguration().getNamespace().getBasePath().getPath();
getConfiguration().getServletContainerAdapter().registerServlet(contextPath, createServlet(router));
该逻辑中调用了createServlet()方法:
protected Servlet createServlet(final Router router) {
return new HttpServlet() {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
……………………
}
};
}
在createServlet()方法中创建了一个HttpServlet对象,其中重写了HttpServlet类中的service()方法,这个方法会在UPnP设备端响应时被调用;
然后会执行一个for循环:
for (Map.Entry<InetAddress, StreamServer> entry : streamServers.entrySet()) {
if (log.isLoggable(Level.FINE))
log.fine("Starting stream server on address: " + entry.getKey());
getConfiguration().getStreamServerExecutorService().execute(entry.getValue());
}
在for循环中执行了execute(),执行体为StreamServer对象,因为StreamServer实现了Runnable,所以我们看一下StreamServer中的run()方法:
public void run() {
getConfiguration().getServletContainerAdapter().startIfNotRunning();
}
在方法中执行了startIfNotRunning()方法,开启了JettyServlet;
至此,Router模块的逻辑就描述完成了,这个是必须的,因为上述逻辑就是用于注册监听指定IP地址和Port端口号的Request;
响应Request(Response)
在上述预备阶段,创建了HttpServlet,用于响应client端的Request,当client发送了Request之后,就会调用了HttpServlet类中重写的service()方法:
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
final long startTime = System.currentTimeMillis();
final int counter = mCounter++;
if (log.isLoggable(Level.FINE))
log.fine(String.format("HttpServlet.service(): id: %3d, request URI: %s", counter, req.getRequestURI()));
AsyncContext async = req.startAsync();
async.setTimeout(getConfiguration().getAsyncTimeoutSeconds()*1000);
async.addListener(new AsyncListener() {
@Override
public void onTimeout(AsyncEvent arg0) throws IOException {
long duration = System.currentTimeMillis() - startTime;
if (log.isLoggable(Level.FINE))
log.fine(String.format("AsyncListener.onTimeout(): id: %3d, duration: %,4d, request: %s", counter, duration, arg0.getSuppliedRequest()));
}
@Override
public void onStartAsync(AsyncEvent arg0) throws IOException {
if (log.isLoggable(Level.FINE))
log.fine(String.format("AsyncListener.onStartAsync(): id: %3d, request: %s", counter, arg0.getSuppliedRequest()));
}
@Override
public void onError(AsyncEvent arg0) throws IOException {
long duration = System.currentTimeMillis() - startTime;
if (log.isLoggable(Level.FINE))
log.fine(String.format("AsyncListener.onError(): id: %3d, duration: %,4d, response: %s", counter, duration, arg0.getSuppliedResponse()));
}
@Override
public void onComplete(AsyncEvent arg0) throws IOException {
long duration = System.currentTimeMillis() - startTime;
if (log.isLoggable(Level.FINE))
log.fine(String.format("AsyncListener.onComplete(): id: %3d, duration: %,4d, response: %s", counter, duration, arg0.getSuppliedResponse()));
}
});
AsyncServletUpnpStream stream =
new AsyncServletUpnpStream(router.getProtocolFactory(), async, req) {
@Override
protected Connection createConnection() {
return new AsyncServletConnection(getRequest());
}
};
router.received(stream);
}
在该方法的最后根据request创建了对应的AsyncServletUpnpStream,重写了AsyncServletUpnpStream类中的createConnection()方法,然后执行了router.received()方法,将创建好的stream传入到该方法中,router就是在AndroidUpnpServiceImpl的onCreate()方法中创建好的Router对象,调用了router的received()方法:
public void received(UpnpStream stream) {
if (!enabled) {
log.fine("Router disabled, ignoring incoming: " + stream);
return;
}
log.fine("Received synchronous stream: " + stream);
getConfiguration().getSyncProtocolExecutorService().execute(stream);
}
在该方法中调用了getSyncProtocolExecutorService(),然后调用execute()方法,我们分开来讲解:
getSyncProtocolExecutorService()
public ExecutorService getSyncProtocolExecutorService() {
return getDefaultExecutorService();
}
protected ExecutorService getDefaultExecutorService() {
return defaultExecutorService;
}
返回的是defaultExecutorService变量,这个变量在DefaultUpnpServiceConfiguration定义,该变量的赋值逻辑在DefaultUpnpServiceConfiguration的构造方法中:
protected DefaultUpnpServiceConfiguration(int streamListenPort, boolean checkRuntime) {
……………………
defaultExecutorService = createDefaultExecutorService();
……………………
}
其实这个构造方法在之前的讲解中描述过,不过是对其他变量初始化的描述,本次分析的是defaultExecutorService变量的初始化;
调用了createDefaultExecutorService()方法:
protected ExecutorService createDefaultExecutorService() {
return new ClingExecutor();
}
其中创建了ClingExecutor对象。ClingExecutor继承自ThreadPoolExecutor,然后重写了afterExecute()方法,即在execute()方法执行完成之后,会调用afterExecute()方法;
AsyncServletUpnpStream.run()
我们看一下stream中的run()方法(stream的类型为AsyncServletUpnpStream,是Runable):
@Override
public void run() {
try {
StreamRequestMessage requestMessage = readRequestMessage();
if (log.isLoggable(Level.FINER))
log.finer("Processing new request message: " + requestMessage);
responseMessage = process(requestMessage);
……………………
} catch (Throwable t) {
……………………
} finally {
complete();
}
}
在该方法中调用了process()方法:
public StreamResponseMessage process(StreamRequestMessage requestMsg) {
log.fine("Processing stream request message: " + requestMsg);
try {
// Try to get a protocol implementation that matches the request message
syncProtocol = getProtocolFactory().createReceivingSync(requestMsg);
} catch (ProtocolCreationException ex) {
log.warning("Processing stream request failed - " + Exceptions.unwrap(ex).toString());
return new StreamResponseMessage(UpnpResponse.Status.NOT_IMPLEMENTED);
}
// Run it
log.fine("Running protocol for synchronous message processing: " + syncProtocol);
syncProtocol.run();
// ... then grab the response
StreamResponseMessage responseMsg = syncProtocol.getOutputMessage();
if (responseMsg == null) {
// That's ok, the caller is supposed to handle this properly (e.g. convert it to HTTP 404)
log.finer("Protocol did not return any response message");
return null;
}
log.finer("Protocol returned response: " + responseMsg);
return responseMsg;
}
在该方法中,调用了getProtocolFactory().createReceivingSync()方法,尝试获得与请求消息匹配的协议实现:
public ReceivingSync createReceivingSync(StreamRequestMessage message) throws ProtocolCreationException {
log.fine("Creating protocol for incoming synchronous: " + message);
if (message.getOperation().getMethod().equals(UpnpRequest.Method.GET)) {
return createReceivingRetrieval(message);
} else if (getUpnpService().getConfiguration().getNamespace().isControlPath(message.getUri())) {
if (message.getOperation().getMethod().equals(UpnpRequest.Method.POST))
return createReceivingAction(message);
} else if (getUpnpService().getConfiguration().getNamespace().isEventSubscriptionPath(message.getUri())) {
if (message.getOperation().getMethod().equals(UpnpRequest.Method.SUBSCRIBE)) {
return createReceivingSubscribe(message);
} else if (message.getOperation().getMethod().equals(UpnpRequest.Method.UNSUBSCRIBE)) {
return createReceivingUnsubscribe(message);
}
} else if (getUpnpService().getConfiguration().getNamespace().isEventCallbackPath(message.getUri())) {
if (message.getOperation().getMethod().equals(UpnpRequest.Method.NOTIFY))
return createReceivingEvent(message);
} else {
……………………
}
throw new ProtocolCreationException("Protocol for message type not found: " + message);
}
因为Action指令控制的类型为POST,所以在该方法中创建了对应的ReceivingAction;
然后调用syncProtocol.run()方法运行同步协议,最后调用syncProtocol.getOutputMessage()方法获取到StreamResponseMessage;
我们紧接着看一下syncProtocol.run()方法:
public void run() {
boolean proceed;
try {
proceed = waitBeforeExecution();
} catch (InterruptedException ex) {
log.info("Protocol wait before execution interrupted (on shutdown?): " + getClass().getSimpleName());
proceed = false;
}
if (proceed) {
try {
execute();
} catch (Exception ex) {
Throwable cause = Exceptions.unwrap(ex);
if (cause instanceof InterruptedException) {
log.log(Level.INFO, "Interrupted protocol '" + getClass().getSimpleName() + "': " + ex, cause);
} else {
throw new RuntimeException(
"Fatal error while executing protocol '" + getClass().getSimpleName() + "': " + ex, ex
);
}
}
}
}
该方法在ReceivingAsync类中实现,核心逻辑是调用了execute()方法,execute()方法为抽象方法,该类的子类为ReceivingSync;
final protected void execute() throws RouterException {
outputMessage = executeSync();
if (outputMessage != null && getRemoteClientInfo().getExtraResponseHeaders().size() > 0) {
log.fine("Setting extra headers on response message: " + getRemoteClientInfo().getExtraResponseHeaders().size());
outputMessage.getHeaders().putAll(getRemoteClientInfo().getExtraResponseHeaders());
}
}
其中调用了executeSync()方法,其中executeSync()方法也是抽象方法,该方法的实现有多个,包括ReceivingAction、ReceivingEvent、ReceivingSubscribe等,字面意思可知,就是分别针对Action、Event以及Subscribe的接收响应处理的逻辑,该阶段,我们是对Action指令进行处理,所以直接分析ReceivingAction中的实现逻辑;
在ReceivingAction类中其实只有一个方法,就是executeSync()方法:
public class ReceivingAction extends ReceivingSync<StreamRequestMessage, StreamResponseMessage> {
final private static Logger log = Logger.getLogger(ReceivingAction.class.getName());
public ReceivingAction(UpnpService upnpService, StreamRequestMessage inputMessage) {
super(upnpService, inputMessage);
}
protected StreamResponseMessage executeSync() throws RouterException{
ContentTypeHeader contentTypeHeader =
getInputMessage().getHeaders().getFirstHeader(UpnpHeader.Type.CONTENT_TYPE, ContentTypeHeader.class);
// Special rules for action messages! UDA 1.0 says:
// 'If the CONTENT-TYPE header specifies an unsupported value (other then "text/xml") the
// device must return an HTTP status code "415 Unsupported Media Type".'
if (contentTypeHeader != null && !contentTypeHeader.isUDACompliantXML()) {
log.warning("Received invalid Content-Type '" + contentTypeHeader + "': " + getInputMessage());
return new StreamResponseMessage(new UpnpResponse(UpnpResponse.Status.UNSUPPORTED_MEDIA_TYPE));
}
if (contentTypeHeader == null) {
log.warning("Received without Content-Type: " + getInputMessage());
}
ServiceControlResource resource =
getUpnpService().getRegistry().getResource(
ServiceControlResource.class,
getInputMessage().getUri()
);
if (resource == null) {
log.fine("No local resource found: " + getInputMessage());
return null;
}
log.fine("Found local action resource matching relative request URI: " + getInputMessage().getUri());
RemoteActionInvocation invocation;
OutgoingActionResponseMessage responseMessage = null;
try {
// Throws ActionException if the action can't be found
IncomingActionRequestMessage requestMessage =
new IncomingActionRequestMessage(getInputMessage(), resource.getModel());
log.finer("Created incoming action request message: " + requestMessage);
invocation = new RemoteActionInvocation(requestMessage.getAction(), getRemoteClientInfo());
// Throws UnsupportedDataException if the body can't be read
log.fine("Reading body of request message");
getUpnpService().getConfiguration().getSoapActionProcessor().readBody(requestMessage, invocation);
log.fine("Executing on local service: " + invocation);
resource.getModel().getExecutor(invocation.getAction()).execute(invocation);
……………………
} catch (ActionException ex) {
……………………
} catch (UnsupportedDataException ex) {
……………………
}
……………………
}
}
在该方法中的核心逻辑为:
resource.getModel().getExecutor(invocation.getAction()).execute(invocation);
我们暂时不关注resource.getModel()这一部分的逻辑,我们直接看getExecutor(invocation.getAction()).execute(invocation);
我们在之前分析发送Action指令的时候,有描述过invocation的逻辑,在这个方法中,invocation变量的类型为RemoteActionInvocation,其实就是client端发送过来的message封装成的RemoteActionInvocation对象:
invocation = new RemoteActionInvocation(requestMessage.getAction(), getRemoteClientInfo());
然后我们开始分析getExecutor()方法,该方法在LocalService类中定义,这就回到了之前创建LocalService的时候了:3
final protected Map<Action, ActionExecutor> actionExecutors;
public ActionExecutor getExecutor(Action action) {
return actionExecutors.get(action);
}
该方法中返回的是actionExecutors.get(action),那我们需要确认这个actionExecutors集合什么时候被赋值的:
public LocalService(ServiceType serviceType, ServiceId serviceId,
Map<Action, ActionExecutor> actionExecutors,
Map<StateVariable, StateVariableAccessor> stateVariableAccessors,
Set<Class> stringConvertibleTypes,
boolean supportsQueryStateVariables) throws ValidationException {
super(serviceType, serviceId,
actionExecutors.keySet().toArray(new Action[actionExecutors.size()]),
stateVariableAccessors.keySet().toArray(new StateVariable[stateVariableAccessors.size()])
);
this.supportsQueryStateVariables = supportsQueryStateVariables;
this.stringConvertibleTypes = stringConvertibleTypes;
this.stateVariableAccessors = stateVariableAccessors;
this.actionExecutors = actionExecutors;
}
actionExecutors的赋值在LocalService的构造方法中赋值的,值是通过构造方法的参数传入的,那我们需要再往上看一下传入逻辑;
我们还记得之前在创建LocalService的逻辑,是通过调用AnnotationLocalServiceBinder.read()方法创建的:
public LocalService read(Class<?> clazz, ServiceId id, ServiceType type,
boolean supportsQueryStateVariables, Set<Class> stringConvertibleTypes)
throws LocalServiceBindingException {
Map<StateVariable, StateVariableAccessor> stateVariables = readStateVariables(clazz, stringConvertibleTypes);
Map<Action, ActionExecutor> actions = readActions(clazz, stateVariables, stringConvertibleTypes);
……………………
try {
return new LocalService(type, id, actions, stateVariables, stringConvertibleTypes, supportsQueryStateVariables);
} catch (ValidationException ex) {
……………………
}
}
在read()方法中创建了相应的LocalService,我们现在分析的是AVTransport Service,其中actions对应的就是actionExecutors集合,而actions是通过调用readActions()方法获取的,其实就是在UPnP设备在注册宣告的时候解析AVTransport Service的时候,解析出来的支持的Action的集合,其中就包含了setAVTransportURI指令。然后通过调用getExecutor(Action action),通过传入的action指令,获取到对应的ActionExecutor对象;
actionExecutors变量的赋值逻辑就解释清楚了,然后我们紧接着调用了execute()方法:
public interface ActionExecutor {
public void execute(final ActionInvocation<LocalService> actionInvocation);
}
AbstractActionExecutor.execute()
ActionExecutor为接口类,实现类为AbstractActionExecutor:
ActionExecutor为接口类,实现类为AbstractActionExecutor:
public void execute(final ActionInvocation<LocalService> actionInvocation) {
log.fine("Invoking on local service: " + actionInvocation);
final LocalService service = actionInvocation.getAction().getService();
try {
if (service.getManager() == null) {
throw new IllegalStateException("Service has no implementation factory, can't get service instance");
}
service.getManager().execute(new Command() {
public void execute(ServiceManager serviceManager) throws Exception {
AbstractActionExecutor.this.execute(
actionInvocation,
serviceManager.getImplementation()
);
}
@Override
public String toString() {
return "Action invocation: " + actionInvocation.getAction();
}
});
} catch (ActionException ex) {
……………………
}
}
在该方法中通过actionInvocation变量获取到了对应的LocalService,这里应该就是AVTransport Service,然后调用了execute()方法,其中创建了Command,用于将请求Action封装成对应的Command:
public interface Command<T> {
public void execute(ServiceManager<T> manager) throws Exception;
}
Command为接口类,其中定义了一个execute()方法,在创建Command对象时实现了该方法,那我们需要确认什么时候执行这个方法:
public void execute(Command<T> cmd) throws Exception {
lock();
try {
cmd.execute(this);
} finally {
unlock();
}
}
其实在DefaultServiceManager的execute()方法中,即AVTransport Service的execute()方法中执行调用了Command对象的execute()方法,所以我们直接看一下Command的execute()方法实现的逻辑:
public void execute(ServiceManager serviceManager) throws Exception {
AbstractActionExecutor.this.execute(
actionInvocation,
serviceManager.getImplementation()
);
}
在该方法中调用了AbstractActionExecutor的execute()方法,AbstractActionExecutor这个类是抽象类,而这个抽象类的实现有两个:MethodActionExecutor和QueryStateVariableExecutor,我们需要确认一下,使用其中哪一个实现类,我们现在需要确认,AbstractActionExecutor.this代表的是什么;
AbstractActionExecutor.this其实代表的就是getExecutor(invocation.getAction()),即上述刚刚提及到的集合,所以我们需要确认一下在LocalService对象创建的时候,创建的是哪种类型的ActionExecutor集合:
public LocalService read(Class<?> clazz, ServiceId id, ServiceType type,
boolean supportsQueryStateVariables, Set<Class> stringConvertibleTypes)
throws LocalServiceBindingException {
Map<StateVariable, StateVariableAccessor> stateVariables = readStateVariables(clazz, stringConvertibleTypes);
Map<Action, ActionExecutor> actions = readActions(clazz, stateVariables, stringConvertibleTypes);
……………………
}
我们看一下readActions()方法,该方法定义在AnnotationLocalServiceBinder中:
protected Map<Action, ActionExecutor> readActions(Class<?> clazz,
Map<StateVariable, StateVariableAccessor> stateVariables,
Set<Class> stringConvertibleTypes)
throws LocalServiceBindingException {
Map<Action, ActionExecutor> map = new HashMap<>();
for (Method method : Reflections.getMethods(clazz, UpnpAction.class)) {
AnnotationActionBinder actionBinder =
new AnnotationActionBinder(method, stateVariables, stringConvertibleTypes);
Action action = actionBinder.appendAction(map);
if(isActionExcluded(action)) {
map.remove(action);
}
}
return map;
}
在该方法中,执行了一个for循环,在这个for循环中,遍历了Reflections.getMethods(clazz, UpnpAction.class),该方法的返回类型为Method,这个很重要,因为在后续的过程中,会使用到这一块的逻辑;
Reflections.getMethods(clazz, UpnpAction.class)方法:
public static List<Method> getMethods(Class clazz, Class annotation) {
List<Method> methods = new ArrayList();
for(Class superClass = clazz; superClass != null && superClass != Object.class; superClass = superClass.getSuperclass()) {
Method[] var4 = superClass.getDeclaredMethods();
int var5 = var4.length;
for(int var6 = 0; var6 < var5; ++var6) {
Method method = var4[var6];
if (method.isAnnotationPresent(annotation)) {
methods.add(method);
}
}
}
return methods;
}
在getMethods()方法中,调用了传入的clazz对象的getDeclaredMethods()方法,这个方法的作用为:获取本类中的所有方法,包括私有的(private、protected、默认以及public)的方法;
那我们就需要确认,clazz代表的是哪个类,我们现在分析的是AVTransport Service,所以我们追溯到创建LocalService的时候:
LocalService<NoboAVTransportService> avTransportService
= mLocalServiceBinder.read(NoboAVTransportService.class);
mAVTransport
= new LastChangeAwareServiceManager<NoboAVTransportService>(avTransportService,
new AVTransportLastChangeParser()) {
@Override
protected NoboAVTransportService createServiceInstance() throws Exception {
return new NoboAVTransportService(mAvTransportLastChange, mMediaPlayers);
}
};
avTransportService.setManager(mAVTransport);
调用了LocalServiceBinder的read()方法创建了AVTransport Service类型的LocalService,read()方法中传入的clazz为NoboAVTransportService.class。NoboAVTransportService为UPnP设备自定义的AVTransport Service,所以我们看一下NoboAVTransportService中的所有方法:
public class NoboAVTransportService extends AbstractAVTransportService {
private static final String TAG = NoboAVTransportService.class.getName();
private Map<UnsignedIntegerFourBytes, NoboMediaPlayer> mPlayers;
public NoboAVTransportService(LastChange lastChange,
Map<UnsignedIntegerFourBytes, NoboMediaPlayer> players) {
……………………
}
public Map<UnsignedIntegerFourBytes, NoboMediaPlayer> getPlayers() {
……………………
}
protected NoboMediaPlayer getInstance(UnsignedIntegerFourBytes instanceId) {
……………………
}
@Override
public void setAVTransportURI(UnsignedIntegerFourBytes instanceId,
String currentURI, String currentURIMetaData)
throws AVTransportException {
……………………
}
……………………
@Override
public void pause(UnsignedIntegerFourBytes instanceId) throws AVTransportException {
……………………
}
@Override
public void record(UnsignedIntegerFourBytes instanceId) throws AVTransportException {
……………………
}
……………………
}
上述就是NoboAVTransportService定义的所有的方法,部分方法省略;
我们紧接着看之后的逻辑:
AnnotationActionBinder actionBinder =
new AnnotationActionBinder(method, stateVariables, stringConvertibleTypes);
Action action = actionBinder.appendAction(map);
调用了appendAction()方法:
public Action appendAction(Map<Action, ActionExecutor> actions) throws LocalServiceBindingException {
String name;
if (getAnnotation().name().length() != 0) {
name = getAnnotation().name();
} else {
name = AnnotationLocalServiceBinder.toUpnpActionName(getMethod().getName());
}
log.fine("Creating action and executor: " + name);
List<ActionArgument> inputArguments = createInputArguments();
Map<ActionArgument<LocalService>, StateVariableAccessor> outputArguments = createOutputArguments();
inputArguments.addAll(outputArguments.keySet());
ActionArgument<LocalService>[] actionArguments =
inputArguments.toArray(new ActionArgument[inputArguments.size()]);
Action action = new Action(name, actionArguments);
ActionExecutor executor = createExecutor(outputArguments);
actions.put(action, executor);
return action;
}
在该方法中调用了createExecutor()方法创建了ActionExecutor对象:
protected ActionExecutor createExecutor(Map<ActionArgument<LocalService>, StateVariableAccessor> outputArguments) {
// TODO: Invent an annotation for this configuration
return new MethodActionExecutor(outputArguments, getMethod());
}
直到现在,我们看到,createExecutor()方法返回的类型为MethodActionExecutor;
至此,AnnotationLocalServiceBinder中的readActions()方法就解析完了;
Command.execute()
readActions()方法分析完成,就可以确定AbstractActionExecutor.this的类型为MethodActionExecutor;
public void execute(ServiceManager serviceManager) throws Exception {
AbstractActionExecutor.this.execute(
actionInvocation,
serviceManager.getImplementation()
);
}
所以我们看一下MethodActionExecutor中的execute()方法:
@Override
protected void execute(ActionInvocation<LocalService> actionInvocation, Object serviceImpl) throws Exception {
Object[] inputArgumentValues = createInputArgumentValues(actionInvocation, method);
……………………
boolean isVoid = method.getReturnType().equals(Void.TYPE);
log.fine("Calling local service method with output arguments: " + method);
Object result;
boolean isArrayResultProcessed = true;
if (isVoid) {
log.fine("Action method is void, calling declared accessors(s) on service instance to retrieve ouput argument(s)");
Reflections.invoke(method, serviceImpl, inputArgumentValues);
result = readOutputArgumentValues(actionInvocation.getAction(), serviceImpl);
} else if (isUseOutputArgumentAccessors(actionInvocation)) {
log.fine("Action method is not void, calling declared accessor(s) on returned instance to retrieve ouput argument(s)");
Object returnedInstance = Reflections.invoke(method, serviceImpl, inputArgumentValues);
result = readOutputArgumentValues(actionInvocation.getAction(), returnedInstance);
} else {
log.fine("Action method is not void, using returned value as (single) output argument");
result = Reflections.invoke(method, serviceImpl, inputArgumentValues);
isArrayResultProcessed = false; // We never want to process e.g. byte[] as individual variable values
}
……………………
}
在该方法中,核心为Reflections.invoke(),即Java反射,通过Method name的方式执行相应的方法,即该方法中的method参数。
那我们需要确认method参数的值,即赋值逻辑:
public MethodActionExecutor(Method method) {
this.method = method;
}
public MethodActionExecutor(Map<ActionArgument<LocalService>, StateVariableAccessor> outputArgumentAccessors, Method method) {
super(outputArgumentAccessors);
this.method = method;
}
method的赋值在MethodActionExecutor的构造方法中,而MethodActionExecutor创建在readActions()方法中的时候创建的,所以我们回看一下之前的逻辑:
protected ActionExecutor createExecutor(Map<ActionArgument<LocalService>, StateVariableAccessor> outputArguments) {
// TODO: Invent an annotation for this configuration
return new MethodActionExecutor(outputArguments, getMethod());
}
在创建MethodActionExecutor对象的时候,传入的是getMethod()方法,我们看一下getMethod()方法逻辑:
public AnnotationActionBinder(Method method, Map<StateVariable, StateVariableAccessor> stateVariables, Set<Class> stringConvertibleTypes) {
this.annotation = method.getAnnotation(UpnpAction.class);
this.stateVariables = stateVariables;
this.method = method;
this.stringConvertibleTypes = stringConvertibleTypes;
}
public Method getMethod() {
return method;
}
method的赋值在AnnotationActionBinder的构造方法中,我们看一下AnnotationActionBinder的创建逻辑,还是readActions()方法中:
protected Map<Action, ActionExecutor> readActions(Class<?> clazz,
Map<StateVariable, StateVariableAccessor> stateVariables,
Set<Class> stringConvertibleTypes)
……………………
for (Method method : Reflections.getMethods(clazz, UpnpAction.class)) {
AnnotationActionBinder actionBinder =
new AnnotationActionBinder(method, stateVariables, stringConvertibleTypes);
……………………
}
……………………
}
其实就是在readActions()方法中将解析出来的method,在创建AnnotationActionBinder对象的时候,传入的method;
至此,method的来源也确定了,所有的Action对应的method也就映射成功了;
Reflections.invoke()
在Command的execute()方法中执行了Reflections.invoke(),根据serviceImpl以及对应的method执行相应的method。
因为我们一直以setAVTransportURI为例,所以我们会调用到NoboAVTransportService方法中的setAVTransportURI()方法:
@Override
public void setAVTransportURI(UnsignedIntegerFourBytes instanceId,
String currentURI, String currentURIMetaData)
throws AVTransportException {
……………………
}
在该方法中获取到了client端传过来的UPnP设备instanceId和媒体内容的uri和metadata信息;
至此,UPnP设备就接收到了client的action指令,类似与play、stop、next、previous等action指令的响应处理类似;
总结
- ControlPointImpl:是控制点的实现类,它有一个execute的方法,来执行控制命令。执行setAVTransportURI其实就是执行控制点的execute(ActionCallback callback)这个方法;
- ActionCallback:继承自Runnable,所有类似SETAVTransportURI的执行都是继承它的,这些指令最后的执行是ActionCallback的run()方法;在run()方法中执行了SendingAction()的方法来发送指令;
- SendingAction:也是继承Runnable,它最后通过sendRemoteRequest()这个方法完成指令的发送;