基于UPnP协议_cling开源库实现分析_控制

614 阅读21分钟

控制

在扫描阶段,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:

image-20220112203504931

其中定义了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个逻辑:

  1. 为ServletContainerAdapter设置ExecutorService,ExecutorService为执行服务器,用于执行Runnable;
  2. 为ServletContainerAdapter添加Connector,用于连接访问执行的ip地址和端口号;
  3. 为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()这个方法完成指令的发送;