宣告&发现
宣告
本质上,宣告就是UPnP设备向局域网中告知本设备的存在,可用于Control Point的控制和显示;
UPnP设备本质上就是一个LocalDevice:
我们首先看一下demo中初始化UPnP设备的逻辑(在NoboMediaRenderer.java):
protected Map<UnsignedIntegerFourBytes, NoboMediaPlayer> mMediaPlayers;
protected LastChange mRenderingControlLastChange;
protected LastChange mAvTransportLastChange;
protected LocalServiceBinder mLocalServiceBinder;
protected ServiceManager<NoboConnectionManagerService> mConnectionManager;
protected LastChangeAwareServiceManager<NoboAVTransportService> mAVTransport;
protected LastChangeAwareServiceManager<NoboAudioRenderingControl> mRenderingControl;
protected LocalDevice mDevice;
public NoboMediaRenderer(int numberOfPlayers, Context context) {
mContext = context;
mLocalServiceBinder = new AnnotationLocalServiceBinder();
mAvTransportLastChange = new LastChange(new AVTransportLastChangeParser());
mRenderingControlLastChange = new LastChange(new RenderingControlLastChangeParser());
mMediaPlayers = new NoboMediaPlayers(context, numberOfPlayers,
mAvTransportLastChange, mRenderingControlLastChange) {
@Override
protected void onPlay(NoboMediaPlayer player) {
}
@Override
protected void onStop(NoboMediaPlayer player) {
}
};
LocalService connectionManagerService
= mLocalServiceBinder.read(NoboConnectionManagerService.class);
mConnectionManager = new DefaultServiceManager(connectionManagerService) {
@Override
protected Object createServiceInstance() throws Exception {
return new NoboConnectionManagerService();
}
};
connectionManagerService.setManager(mConnectionManager);
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);
LocalService<NoboAudioRenderingControl> renderingControlService
= mLocalServiceBinder.read(NoboAudioRenderingControl.class);
mRenderingControl
= new LastChangeAwareServiceManager<NoboAudioRenderingControl>(renderingControlService,
new AVTransportLastChangeParser()) {
@Override
protected NoboAudioRenderingControl createServiceInstance() throws Exception {
return new NoboAudioRenderingControl(mRenderingControlLastChange, mMediaPlayers);
}
};
renderingControlService.setManager(mRenderingControl);
try {
UDN udn = UpnpProfiles.uniqueSystemIdentifier(UpnpProfiles.UPNP_SALT);
mDevice = new LocalDevice(new DeviceIdentity(udn),
new UDADeviceType(DeviceProfiles.UDA_DEVICE_TYPE, DeviceProfiles.UDA_DEVICE_VERSION),
new DeviceDetails(
DeviceProfiles.DEVICE_FRIENDLY_NAME,
new ManufacturerDetails("Cling", Build.MANUFACTURER),
new ModelDetails(DeviceProfiles.DMR_MODEL_NAME, DeviceProfiles.DMR_MODEL_DESC,
DeviceProfiles.DMR_MODEL_NUMBER, DeviceProfiles.DMR_MODEL_URL),
new DLNADoc[] {
new DLNADoc(DeviceProfiles.DEV_CLASS, DLNADoc.Version.V1_5)
},
new DLNACaps(new String[] {
DeviceProfiles.DLNA_CAPS_AV,
DeviceProfiles.DLNA_CAPS_IMAGE,
DeviceProfiles.DLNA_CAPS_AUDIO
})
),
new Icon[] {
createDefaultDeviceIcon()
},
new LocalService[]{
avTransportService,
renderingControlService,
connectionManagerService
});
} catch (Exception ex) {
throw new RuntimeException(ex);
}
runLastChangePushThread();
}
该构造方法中,一共执行了5个大的逻辑:
- 初始化ConnectionManager Service;
- 初始化AVTransport Service;
- 初始化RenderingControl Service;
- 初始化LocalDevice(UPnP设备);
- LocalDevice发布;
初始化ConnectionManager Service
初始化ConnectionManagerService类型的LocalService
在该构造方法中,首先初始化了mLocalServiceBinder变量,该变量的类型为AnnotationLocalServiceBinder;
protected LocalServiceBinder mLocalServiceBinder;
mLocalServiceBinder = new AnnotationLocalServiceBinder();
然后使用mLocalServiceBinder变量来创建LocalService对象;
LocalService connectionManagerService
= mLocalServiceBinder.read(NoboConnectionManagerService.class);
mConnectionManager = new DefaultServiceManager(connectionManagerService) {
@Override
protected Object createServiceInstance() throws Exception {
return new NoboConnectionManagerService();
}
};
connectionManagerService.setManager(mConnectionManager);
在该过程中,首先通过mLocalServiceBinder的read()方法,传入需要创建的对象的class,创建对应的LocalService对象;
public LocalService read(Class<?> clazz) throws LocalServiceBindingException {
log.fine("Reading and binding annotations of service implementation class: " + clazz);
// Read the service ID and service type from the annotation
if (clazz.isAnnotationPresent(UpnpService.class)) {
// 获取指定注释类型的注释,该方法以对象的形式返回该类
UpnpService annotation = clazz.getAnnotation(UpnpService.class);
UpnpServiceId idAnnotation = annotation.serviceId();
UpnpServiceType typeAnnotation = annotation.serviceType();
ServiceId serviceId = idAnnotation.namespace().equals(UDAServiceId.DEFAULT_NAMESPACE)
? new UDAServiceId(idAnnotation.value())
: new ServiceId(idAnnotation.namespace(), idAnnotation.value());
ServiceType serviceType = typeAnnotation.namespace().equals(UDAServiceType.DEFAULT_NAMESPACE)
? new UDAServiceType(typeAnnotation.value(), typeAnnotation.version())
: new ServiceType(typeAnnotation.namespace(), typeAnnotation.value(), typeAnnotation.version());
boolean supportsQueryStateVariables = annotation.supportsQueryStateVariables();
Set<Class> stringConvertibleTypes = readStringConvertibleTypes(annotation.stringConvertibleTypes());
return read(clazz, serviceId, serviceType, supportsQueryStateVariables, stringConvertibleTypes);
} else {
throw new LocalServiceBindingException("Given class is not an @UpnpService");
}
}
在该方法中根据annotation获取的idAnnotation和typeAnnotation来分别确定serviceId和serviceType;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UpnpService {
UpnpServiceId serviceId();
UpnpServiceType serviceType();
boolean supportsQueryStateVariables() default true;
Class[] stringConvertibleTypes() default {};
}
然后创建supportsQueryStateVariables和stringConvertibleTypes,idAnnotation、typeAnnotation以及stringConvertibleTypes定义在ConnectionManagerService.java中:
@UpnpService(
serviceId = @UpnpServiceId("ConnectionManager"),
serviceType = @UpnpServiceType(value = "ConnectionManager", version = 1),
stringConvertibleTypes = {ProtocolInfo.class, ProtocolInfos.class, ServiceReference.class}
)
最后将这个参数传入到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);
// Special treatment of the state variable querying action
if (supportsQueryStateVariables) {
actions.put(new QueryStateVariableAction(), new QueryStateVariableExecutor());
}
try {
return new LocalService(type, id, actions, stateVariables, stringConvertibleTypes, supportsQueryStateVariables);
} catch (ValidationException ex) {
log.severe("Could not validate device model: " + ex.toString());
for (ValidationError validationError : ex.getErrors()) {
log.severe(validationError.toString());
}
throw new LocalServiceBindingException("Validation of model failed, check the log");
}
}
在该read()方法中,调用了readStateVariables()方法和readActions()方法,readStateVariables()用于获取所有支持的state variables,readActions()用于获取所有支持的Action的集合;
最后return new了LocalService对象;
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;
}
将传入的变量保存到LocalService对象中;
其中执行了super()方法,我们看一下LocalService的父类Service的构造方法:
public Service(ServiceType serviceType, ServiceId serviceId,
Action<S>[] actions, StateVariable<S>[] stateVariables) throws ValidationException {
this.serviceType = serviceType;
this.serviceId = serviceId;
if (actions != null) {
for (Action action : actions) {
this.actions.put(action.getName(), action);
action.setService(this);
}
}
if (stateVariables != null) {
for (StateVariable stateVariable : stateVariables) {
this.stateVariables.put(stateVariable.getName(), stateVariable);
stateVariable.setService(this);
}
}
}
其中将之前获取到的state variables和action一一保存到Map集合中;
至此,LocalService的创建过程就分析完了;
初始化ServiceManager
LocalService对象创建成功之后,就需要初始化mConnectionManager对象,该对象类型为ServiceManager,初始化逻辑为:
mConnectionManager = new DefaultServiceManager(connectionManagerService) {
@Override
protected Object createServiceInstance() throws Exception {
return new NoboConnectionManagerService();
}
};
创建了一个DefaultServiceManager对象,该构造方法中传入了刚刚创建好的LocalService:
protected DefaultServiceManager(LocalService<T> service) {
this(service, null);
}
public DefaultServiceManager(LocalService<T> service, Class<T> serviceClass) {
this.service = service;
this.serviceClass = serviceClass;
}
将传入的LocalService保存到了DefaultServiceManager对象的service变量中;
在创建DefaultServiceManager对象的过程中,重写了createServiceInstance()方法,使之该方法返回new NoboConnectionManagerService();
NoboConnectionManager.java的构造方法中加载了UPnP协议所支持的媒体数据格式,包括image、audio、video;
保存ServiceManager
ServiceManager对象,即mConnectionManager变量初始化成功之后,紧接着调用了:
connectionManagerService.setManager(mConnectionManager);
将初始化好的mConnectionManager保存到connectionManagerService LocalService对象中:
synchronized public void setManager(ServiceManager<T> manager) {
if (this.manager != null) {
throw new IllegalStateException("Manager is final");
}
this.manager = manager;
}
至此,整个ConnectionManager Service的初始化过程就分析完成了;
初始化AVTransport Service
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);
其实AVTransport Service的创建方式也是类同,首先根据LocalServiceBinder.read()方法创建了AVTransportService类型的LocalService,与ConnectionManager Service稍微有点不同的是,mAVTransport变量的类型为LastChangeAwareServiceManager,LastChangeAwareServiceManager用于处理(收集和传递)GENA订阅服务事件的服务,其实LastChangeAwareServiceManager本质上是继承自DefaultServiceManager,在DefaultServiceManager的基础上增加了Event处理的机制;
核心逻辑:增加了Event处理的机制即为之后要讲到的Event订阅逻辑;
public LastChangeAwareServiceManager(LocalService<T> localService,
LastChangeParser lastChangeParser) {
this(localService, null, lastChangeParser);
}
public LastChangeAwareServiceManager(LocalService<T> localService,
Class<T> serviceClass,
LastChangeParser lastChangeParser) {
super(localService, serviceClass);
this.lastChangeParser = lastChangeParser;
}
在初始化LastChangeAwareServiceManager对象时,传入了一个new AVTransportLastChangeParser(),该对象主要是状态机,用于管理AVTransport的状态;
protected NoboAVTransportService createServiceInstance() throws Exception {
return new NoboAVTransportService(mAvTransportLastChange, mMediaPlayers);
}
紧接着也是重写createServiceInstance()方法,定义返回NoboAVTransportService对象;
avTransportService.setManager(mAVTransport);
最后调用setManager,将创建好的状态机管理对象保存到LocalService中;
初始化RenderingControl Service
同上,不再做过多的称述;
初始化LocalDevice(UPnP设备)
创建LocalDevice对象
3种类型的LocalService创建完成之后,就需要创建对应的LocalDevice,LocalDevice包含上述的3个LocalService;
try {
UDN udn = UpnpProfiles.uniqueSystemIdentifier(UpnpProfiles.UPNP_SALT);
mDevice = new LocalDevice(new DeviceIdentity(udn),
new UDADeviceType(DeviceProfiles.UDA_DEVICE_TYPE, DeviceProfiles.UDA_DEVICE_VERSION),
new DeviceDetails(
DeviceProfiles.DEVICE_FRIENDLY_NAME,
new ManufacturerDetails("Cling", Build.MANUFACTURER),
new ModelDetails(DeviceProfiles.DMR_MODEL_NAME, DeviceProfiles.DMR_MODEL_DESC,
DeviceProfiles.DMR_MODEL_NUMBER, DeviceProfiles.DMR_MODEL_URL),
new DLNADoc[] {
new DLNADoc(DeviceProfiles.DEV_CLASS, DLNADoc.Version.V1_5)
},
new DLNACaps(new String[] {
DeviceProfiles.DLNA_CAPS_AV,
DeviceProfiles.DLNA_CAPS_IMAGE,
DeviceProfiles.DLNA_CAPS_AUDIO
})
),
new Icon[] {
createDefaultDeviceIcon()
},
new LocalService[]{
avTransportService,
renderingControlService,
connectionManagerService
});
} catch (Exception ex) {
throw new RuntimeException(ex);
}
首先先对上述涉及到的类进行简单的说明:
| 类 | 说明 |
|---|---|
| UDN | 唯一的设备名称,设备的通用唯一标识符 |
| DeviceIdentity | 用于保存LocalDevice的UDN以及广播的最大有效时长 |
| UDADeviceType | 用于定义具有固定的schemas-upnp-org命名空间的设备类型 |
| DeviceDetails | 用于描述LocalDevice详细信息 |
| ManufacturerDetails | 用于封装关于设备制造商的可选元数据 |
| ModelDetails | 用于封装关于设备模型的可选元数据 |
| DLNADoc | 用于表示DLNA文档及其版本 |
| DLNACaps | 用于表示DLNA功能 |
| Icon | 定义本地设备图标 |
解释完上述类之后,我们看一下LocalDevice的构造方法:
public LocalDevice(DeviceIdentity identity, DeviceType type, DeviceDetails details,
Icon[] icons, LocalService[] services) throws ValidationException {
super(identity, type, details, icons, services);
this.deviceDetailsProvider = null;
}
LocalDevice继承自Device:
public Device(DI identity, DeviceType type, DeviceDetails details,
Icon[] icons, S[] services) throws ValidationException {
this(identity, null, type, details, icons, services, null);
}
public Device(DI identity, UDAVersion version, DeviceType type, DeviceDetails details,
Icon[] icons, S[] services, D[] embeddedDevices) throws ValidationException {
this.identity = identity;
this.version = version == null ? new UDAVersion() : version;
this.type = type;
this.details = details;
// We don't fail device validation if icons were invalid, only log a warning. To
// comply with mutability rules (can't set icons field in validate() method), we
// validate the icons here before we set the field value
List<Icon> validIcons = new ArrayList<>();
if (icons != null) {
for (Icon icon : icons) {
if (icon != null) {
icon.setDevice(this); // Set before validate()!
List<ValidationError> iconErrors = icon.validate();
if(iconErrors.isEmpty()) {
validIcons.add(icon);
} else {
log.warning("Discarding invalid '" + icon + "': " + iconErrors);
}
}
}
}
this.icons = validIcons.toArray(new Icon[validIcons.size()]);
boolean allNullServices = true;
if (services != null) {
for (S service : services) {
if (service != null) {
allNullServices = false;
service.setDevice(this);
}
}
}
this.services = services == null || allNullServices ? null : services;
boolean allNullEmbedded = true;
// 传入的embeddedDevices变量为null,所以不会执行该if判断
if (embeddedDevices != null) {
for (D embeddedDevice : embeddedDevices) {
if (embeddedDevice != null) {
allNullEmbedded = false;
embeddedDevice.setParentDevice(this);
}
}
}
this.embeddedDevices = embeddedDevices == null || allNullEmbedded ? null : embeddedDevices;
List<ValidationError> errors = validate();
if (errors.size() > 0) {
if (log.isLoggable(Level.FINEST)) {
for (ValidationError error : errors) {
log.finest(error.toString());
}
}
throw new ValidationException("Validation of device graph failed, call getErrors() on exception", errors);
}
}
上述主要就是对传入的参数进行保存和遍历处理,主要是对Icon和Services的处理;
boolean allNullServices = true;
if (services != null) {
for (S service : services) {
if (service != null) {
allNullServices = false;
service.setDevice(this);
}
}
}
void setDevice(D device) {
if (this.device != null)
throw new IllegalStateException("Final value has been set already, model is immutable");
this.device = device;
}
使LocalService的对象中也持有的LocalDevice的句柄;
至此,LocalDevice对象就创建完成了,之后会创建一个线程,然后一直执行如下逻辑:
new Thread() {
@Override
public void run() {
try {
while(true) {
mAVTransport.fireLastChange();
mRenderingControl.fireLastChange();
Thread.sleep(500);
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}.start();
首先我们看一下mAVTransport.fireLastChange():
/**
* Call this method to propagate all accumulated "LastChange" values to GENA subscribers.
*/
public void fireLastChange() {
// We need to obtain locks in the right order to avoid deadlocks:
// 1. The lock() of the DefaultServiceManager
// 2. The monitor/synchronized of the LastChange.fire() method
lock();
try {
getImplementation().getLastChange().fire(getPropertyChangeSupport());
} finally {
unlock();
}
}
调用此方法将所有累积的“LastChange”值传播到GENA订阅者,因为在创建上述3种LocalService的时候,就已经创建好了对应的订阅对象,在之后client连接UPnP设备时,会创建对应的事件订阅逻辑,上述方法就是用于处理之后事件订阅推送的逻辑;
我们依次看一下上述逻辑:
getImplementation()
public T getImplementation() {
lock();
try {
if (serviceImpl == null) {
init();
}
return serviceImpl;
} finally {
unlock();
}
}
首次执行的时候,serviceImpl应该空,在为空的情况下,会执行init()方法:
protected void init() {
log.fine("No service implementation instance available, initializing...");
try {
// The actual instance we ware going to use and hold a reference to (1:1 instance for manager)
serviceImpl = createServiceInstance();
// How the implementation instance will tell us about property changes
propertyChangeSupport = createPropertyChangeSupport(serviceImpl);
propertyChangeSupport.addPropertyChangeListener(createPropertyChangeListener(serviceImpl));
} catch (Exception ex) {
throw new RuntimeException("Could not initialize implementation: " + ex, ex);
}
}
在该方法中首先执行了createServiceInstance()方法,这个方法就是我们上述刚刚重新的createServiceInstance()方法,返回的是我们自定义的Service对象,然后就是配置属性监听逻辑;
getLastChange()
@Override
public LastChange getLastChange() {
return lastChange;
}
返回的是LastChange对象,该对象的赋值逻辑在AbstractAVTransportService的构造方法中:
protected AbstractAVTransportService(LastChange lastChange) {
this.propertyChangeSupport = new PropertyChangeSupport(this);
this.lastChange = lastChange;
}
而NoboAVTransportService继承自AbstractAVTransportService,并执行了super(),NoboAVTransportService的创建在重新的createServiceInstance()方法中执行的;
fire(getPropertyChangeSupport())
synchronized public void fire(PropertyChangeSupport propertyChangeSupport) {
String lastChanges = toString();
if (lastChanges != null && lastChanges.length() > 0) {
propertyChangeSupport.firePropertyChange("LastChange", previousValue, lastChanges);
reset();
}
}
在该方法中执行了toString(),该方法主要用于获取UPnP设备发送的event事件集合,如果为空,说明当前没有状态发生变化,否则执行propertyChangeSupport.firePropertyChange()方法,propertyChangeSupport对象即上述init()方法中初始化的propertyChangeSupport对象:
propertyChangeSupport.firePropertyChange("LastChange", previousValue, lastChanges);
这一块逻辑涉及到了Android Sdk中的流程,暂不做分析;
至此,LocalDevice的创建初始化就完成了,就可以将LocalDevice发布到局域网中;
LocalDevice发布
mUpnpService = (AndroidUpnpService) service;
NoboMediaRenderer mediaRenderer = new NoboMediaRenderer(1, getApplicationContext());
mUpnpService.getRegistry().addDevice(mediaRenderer.getDevice());
在发布逻辑中,首先先获取AndroidUpnpService,连接成功之后,在onServiceConnected()方法中执行上述逻辑;
通过NoboMediaRenderer对象,获取到LocalDevice实例,即上述刚刚初始化好的LocalDevice对象;
然后通过mUpnpService.getRegistry()获取到UpnpService中的注册表:
protected class Binder extends android.os.Binder implements AndroidUpnpService {
public UpnpService get() {
return upnpService;
}
public UpnpServiceConfiguration getConfiguration() {
return upnpService.getConfiguration();
}
public Registry getRegistry() {
return upnpService.getRegistry();
}
public ControlPoint getControlPoint() {
return upnpService.getControlPoint();
}
}
getRegistry()
其中调用了upnpService.getRegistry(),upnpService对象类型为UpnpServiceImpl,UpnpServiceImpl实现了UpnpService接口类:
public Registry getRegistry() {
return registry;
}
而registry的初始化逻辑在UpnpServiceImpl的构造方法中:
public UpnpServiceImpl(UpnpServiceConfiguration configuration, RegistryListener... registryListeners) {
this.configuration = configuration;
log.info(">>> Starting UPnP service...");
log.info("Using configuration: " + getConfiguration().getClass().getName());
// Instantiation order is important: Router needs to start its network services after registry is ready
this.protocolFactory = createProtocolFactory();
this.registry = createRegistry(protocolFactory);
for (RegistryListener registryListener : registryListeners) {
this.registry.addListener(registryListener);
}
this.router = createRouter(protocolFactory, registry);
try {
this.router.enable();
} catch (RouterException ex) {
throw new RuntimeException("Enabling network router failed: " + ex, ex);
}
this.controlPoint = createControlPoint(protocolFactory, registry);
log.info("<<< UPnP service started successfully");
}
调用了createRegistry()方法:
protected Registry createRegistry(ProtocolFactory protocolFactory) {
return new RegistryImpl(this);
}
返回了一个RegistryImpl对象:
@Inject
public RegistryImpl(UpnpService upnpService) {
log.fine("Creating Registry: " + getClass().getName());
this.upnpService = upnpService;
log.fine("Starting registry background maintenance...");
registryMaintainer = createRegistryMaintainer();
if (registryMaintainer != null) {
getConfiguration().getRegistryMaintainerExecutor().execute(registryMaintainer);
}
}
调用了createRegistryMaintainer()方法,这个方法主要是用于创建注册表容器,该方法的返回类型为Runnable;
protected RegistryMaintainer createRegistryMaintainer() {
return new RegistryMaintainer(
this,
getConfiguration().getRegistryMaintenanceIntervalMillis()
);
}
然后紧接着调用了getConfiguration().getRegistryMaintainerExecutor().execute(registryMaintainer)逻辑,该逻辑主要是用于启动registry;
因为RegistryMaintainer的类型为Runnable,所以我们需要关注一下run()方法:
public void run() {
stopped = false;
if (log.isLoggable(Level.FINE))
log.fine("Running registry maintenance loop every milliseconds: " + sleepIntervalMillis);
while (!stopped) {
try {
registry.maintain();
Thread.sleep(sleepIntervalMillis);
} catch (InterruptedException ex) {
stopped = true;
}
}
log.fine("Stopped status on thread received, ending maintenance loop");
}
在run()中通过while控制逻辑,判断条件为stopped,默认为false,该变量只有在调用stop()方法的时候,才会被值为true;
在run()方法中调用了registry.maintain()方法:
synchronized void maintain() {
if (log.isLoggable(Level.FINEST))
log.finest("Maintaining registry...");
// Remove expired resources
Iterator<RegistryItem<URI, Resource>> it = resourceItems.iterator();
while (it.hasNext()) {
RegistryItem<URI, Resource> item = it.next();
if (item.getExpirationDetails().hasExpired()) {
if (log.isLoggable(Level.FINER))
log.finer("Removing expired resource: " + item);
it.remove();
}
}
// Let each resource do its own maintenance
for (RegistryItem<URI, Resource> resourceItem : resourceItems) {
resourceItem.getItem().maintain(
pendingExecutions,
resourceItem.getExpirationDetails()
);
}
// These add all their operations to the pendingExecutions queue
remoteItems.maintain();
localItems.maintain();
// We now run the queue asynchronously so the maintenance thread can continue its loop undisturbed
runPendingExecutions(true);
}
在该方法中执行了remoteItems.maintain()和localItems.maintain()方法,目前remoteItems和localItems都为空,在后续addDevice()中,会向localItems中添加创建好的LocalDevice;
所以我们看一下maintain()方法:
void maintain() {
if(getDeviceItems().isEmpty()) return ;
Set<RegistryItem<UDN, LocalDevice>> expiredLocalItems = new HashSet<>();
// "Flooding" is enabled, check if we need to send advertisements for all devices
int aliveIntervalMillis = registry.getConfiguration().getAliveIntervalMillis();
if(aliveIntervalMillis > 0) {
long now = System.currentTimeMillis();
if(now - lastAliveIntervalTimestamp > aliveIntervalMillis) {
lastAliveIntervalTimestamp = now;
for (RegistryItem<UDN, LocalDevice> localItem : getDeviceItems()) {
if (isAdvertised(localItem.getKey())) {
log.finer("Flooding advertisement of local item: " + localItem);
expiredLocalItems.add(localItem);
}
}
}
} else {
// Reset, the configuration might dynamically switch the alive interval
lastAliveIntervalTimestamp = 0;
// Alive interval is not enabled, regular expiration check of all devices
for (RegistryItem<UDN, LocalDevice> localItem : getDeviceItems()) {
if (isAdvertised(localItem.getKey()) && localItem.getExpirationDetails().hasExpired(true)) {
log.finer("Local item has expired: " + localItem);
expiredLocalItems.add(localItem);
}
}
}
// Now execute the advertisements
for (RegistryItem<UDN, LocalDevice> expiredLocalItem : expiredLocalItems) {
log.fine("Refreshing local device advertisement: " + expiredLocalItem.getItem());
advertiseAlive(expiredLocalItem.getItem());
expiredLocalItem.getExpirationDetails().stampLastRefresh();
}
// Expire incoming subscriptions
Set<RegistryItem<String, LocalGENASubscription>> expiredIncomingSubscriptions = new HashSet<>();
for (RegistryItem<String, LocalGENASubscription> item : getSubscriptionItems()) {
if (item.getExpirationDetails().hasExpired(false)) {
expiredIncomingSubscriptions.add(item);
}
}
for (RegistryItem<String, LocalGENASubscription> subscription : expiredIncomingSubscriptions) {
log.fine("Removing expired: " + subscription);
removeSubscription(subscription.getItem());
subscription.getItem().end(CancelReason.EXPIRED);
}
}
在该方法中执行了3个for循环,我们主要关注第一个for循环,在第一个for循环中,执行了advertiseAlive(expiredLocalItem.getItem())方法:
protected void advertiseAlive(final LocalDevice localDevice) {
registry.executeAsyncProtocol(new Runnable() {
public void run() {
try {
log.finer("Sleeping some milliseconds to avoid flooding the network with ALIVE msgs");
Thread.sleep(randomGenerator.nextInt(100));
} catch (InterruptedException ex) {
log.severe("Background execution interrupted: " + ex.getMessage());
}
registry.getProtocolFactory().createSendingNotificationAlive(localDevice).run();
}
});
}
在该方法中执行了registry.getProtocolFactory().createSendingNotificationAlive(localDevice).run(),其中getProtocolFactory()返回的是一个协议工厂:
public SendingNotificationAlive createSendingNotificationAlive(LocalDevice localDevice) {
return new SendingNotificationAlive(getUpnpService(), localDevice);
}
创建了SendingNotificationAlive对象:
public SendingNotificationAlive(UpnpService upnpService, LocalDevice device) {
super(upnpService, device);
}
SendingNotificationAlive继承自SendingNotification:
public SendingNotification(UpnpService upnpService, LocalDevice device) {
super(upnpService);
this.device = device;
}
SendingNotification继承自SendingAsync:
protected SendingAsync(UpnpService upnpService) {
this.upnpService = upnpService;
}
上述调用了registry.getProtocolFactory().createSendingNotificationAlive(localDevice).run(),run()在SendingAsync中定义:
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
);
}
}
}
该方法中调用了execute()方法,该方法在SendingNotificationAlive中实现,而在SendingNotificationAlive中的execute最终调用了SendingNotification中的execute()方法:
protected void execute() throws RouterException {
List<NetworkAddress> activeStreamServers =
getUpnpService().getRouter().getActiveStreamServers(null);
if (activeStreamServers.size() == 0) {
log.fine("Aborting notifications, no active stream servers found (network disabled?)");
return;
}
// Prepare it once, it's the same for each repetition
List<Location> descriptorLocations = new ArrayList<>();
for (NetworkAddress activeStreamServer : activeStreamServers) {
descriptorLocations.add(
new Location(
activeStreamServer,
getUpnpService().getConfiguration().getNamespace().getDescriptorPathString(getDevice())
)
);
}
for (int i = 0; i < getBulkRepeat(); i++) {
try {
for (Location descriptorLocation : descriptorLocations) {
sendMessages(descriptorLocation);
}
// UDA 1.0 is silent about this but UDA 1.1 recomments "a few hundred milliseconds"
log.finer("Sleeping " + getBulkIntervalMilliseconds() + " milliseconds");
Thread.sleep(getBulkIntervalMilliseconds());
} catch (InterruptedException ex) {
log.warning("Advertisement thread was interrupted: " + ex);
}
}
}
该方法中执行了sendMessages()方法:
public void sendMessages(Location descriptorLocation) throws RouterException {
log.finer("Sending root device messages: " + getDevice());
List<OutgoingNotificationRequest> rootDeviceMsgs =
createDeviceMessages(getDevice(), descriptorLocation);
for (OutgoingNotificationRequest upnpMessage : rootDeviceMsgs) {
getUpnpService().getRouter().send(upnpMessage);
}
if (getDevice().hasEmbeddedDevices()) {
for (LocalDevice embeddedDevice : getDevice().findEmbeddedDevices()) {
log.finer("Sending embedded device messages: " + embeddedDevice);
List<OutgoingNotificationRequest> embeddedDeviceMsgs =
createDeviceMessages(embeddedDevice, descriptorLocation);
for (OutgoingNotificationRequest upnpMessage : embeddedDeviceMsgs) {
getUpnpService().getRouter().send(upnpMessage);
}
}
}
List<OutgoingNotificationRequest> serviceTypeMsgs =
createServiceTypeMessages(getDevice(), descriptorLocation);
if (serviceTypeMsgs.size() > 0) {
log.finer("Sending service type messages");
for (OutgoingNotificationRequest upnpMessage : serviceTypeMsgs) {
getUpnpService().getRouter().send(upnpMessage);
}
}
}
在该方法中,总共有3个List,rootDeviceMsgs、embeddedDeviceMsgs、serviceTypeMsgs,分别对应3种类型的device:root device、embedded device以及对应的services list of the embedded device,每个List的类型为OutgoingNotificationRequest,我们看一下OutgoingNotificationRequest的构造方法:
protected OutgoingNotificationRequest(Location location, LocalDevice device, NotificationSubtype type) {
super(
new UpnpRequest(UpnpRequest.Method.NOTIFY),
ModelUtil.getInetAddressByName(Constants.IPV4_UPNP_MULTICAST_GROUP),
Constants.UPNP_MULTICAST_PORT
);
this.type = type;
getHeaders().add(UpnpHeader.Type.MAX_AGE, new MaxAgeHeader(device.getIdentity().getMaxAgeSeconds()));
getHeaders().add(UpnpHeader.Type.LOCATION, new LocationHeader(location.getURL()));
getHeaders().add(UpnpHeader.Type.SERVER, new ServerHeader());
getHeaders().add(UpnpHeader.Type.HOST, new HostHeader());
getHeaders().add(UpnpHeader.Type.NTS, new NTSHeader(type));
}
在该构造方法中定义了该request的method为NOTIFY,意为通知多播地址,宣告的意思;
在每个对应的for循环中执行了getUpnpService().getRouter().send(upnpMessage)方法,该upnpMessage中包含了上述描述的request,意为向多播地址宣告该device的可见和可用性;
紧接着调用了getUpnpService().getRouter().send(upnpMessage),Router为网络传输层接口,通过调用send()方法发送,Router的实现类为RouterImpl:
public void send(OutgoingDatagramMessage msg) throws RouterException {
lock(readLock);
try {
if (enabled) {
for (DatagramIO datagramIO : datagramIOs.values()) {
datagramIO.send(msg);
}
} else {
log.fine("Router disabled, not sending datagram: " + msg);
}
} finally {
unlock(readLock);
}
}
调用了datagramIO.send()方法:
synchronized public void send(OutgoingDatagramMessage message) {
if (log.isLoggable(Level.FINE)) {
log.fine("Sending message from address: " + localAddress);
}
DatagramPacket packet = datagramProcessor.write(message);
if (log.isLoggable(Level.FINE)) {
log.fine("Sending UDP datagram packet to: " + message.getDestinationAddress() + ":" + message.getDestinationPort());
}
send(packet);
}
调用了datagramProcessor.write(message),datagramProcessor就是DefaultUpnpServiceConfiguration构造方法中初始化的变量:
public DatagramPacket write(OutgoingDatagramMessage message) throws UnsupportedDataException {
StringBuilder statusLine = new StringBuilder();
UpnpOperation operation = message.getOperation();
if (operation instanceof UpnpRequest) {
UpnpRequest requestOperation = (UpnpRequest) operation;
statusLine.append(requestOperation.getHttpMethodName()).append(" * ");
statusLine.append("HTTP/1.").append(operation.getHttpMinorVersion()).append("\r\n");
} else if (operation instanceof UpnpResponse) {
UpnpResponse responseOperation = (UpnpResponse) operation;
statusLine.append("HTTP/1.").append(operation.getHttpMinorVersion()).append(" ");
statusLine.append(responseOperation.getStatusCode()).append(" ").append(responseOperation.getStatusMessage());
statusLine.append("\r\n");
} else {
throw new UnsupportedDataException(
"Message operation is not request or response, don't know how to process: " + message
);
}
// UDA 1.0, 1.1.2: No body but message must have a blank line after header
StringBuilder messageData = new StringBuilder();
messageData.append(statusLine);
messageData.append(message.getHeaders().toString()).append("\r\n");
if (log.isLoggable(Level.FINER)) {
log.finer("Writing message data for: " + message);
log.finer("---------------------------------------------------------------------------------");
log.finer(messageData.toString().substring(0, messageData.length() - 2)); // Don't print the blank lines
log.finer("---------------------------------------------------------------------------------");
}
try {
// According to HTTP 1.0 RFC, headers and their values are US-ASCII
// TODO: Probably should look into escaping rules, too
byte[] data = messageData.toString().getBytes("US-ASCII");
log.fine("Writing new datagram packet with " + data.length + " bytes for: " + message);
return new DatagramPacket(data, data.length, message.getDestinationAddress(), message.getDestinationPort());
} catch (UnsupportedEncodingException ex) {
throw new UnsupportedDataException(
"Can't convert message content to US-ASCII: " + ex.getMessage(), ex, messageData
);
}
}
在该方法中对message进行解析,封装了对应格式的数据报包;
最后调用了send()方法:
synchronized public void send(DatagramPacket datagram) {
if (log.isLoggable(Level.FINE)) {
log.fine("Sending message from address: " + localAddress);
}
try {
socket.send(datagram);
} catch (SocketException ex) {
log.fine("Socket closed, aborting datagram send to: " + datagram.getAddress());
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
log.log(Level.SEVERE, "Exception sending datagram to: " + datagram.getAddress() + ": " + ex, ex);
}
}
在该方法中使用了socket的send()方法发送了数据报包,socket的类型为DatagramSocket;
至此,通过多播套接字发送了发现广播;
上述逻辑,只是推送消息逻辑,但是还没有真正执行,真正执行的逻辑在addDevice()方法中触发;
addDevice()
注册表创建成功并启动之后,紧接着调用了addDevice()方法:
synchronized public void addDevice(LocalDevice localDevice) {
localItems.add(localDevice);
}
将创建好的LocalDevice添加到了localItems中,localItems的类型为LocalItems:
void add(LocalDevice localDevice) throws RegistrationException {
add(localDevice, null);
}
void add(final LocalDevice localDevice, DiscoveryOptions options) throws RegistrationException {
// Always set/override the options, even if we don't end up adding the device
setDiscoveryOptions(localDevice.getIdentity().getUdn(), options);
if (registry.getDevice(localDevice.getIdentity().getUdn(), false) != null) {
log.fine("Ignoring addition, device already registered: " + localDevice);
return;
}
log.fine("Adding local device to registry: " + localDevice);
for (Resource deviceResource : getResources(localDevice)) {
if (registry.getResource(deviceResource.getPathQuery()) != null) {
throw new RegistrationException("URI namespace conflict with already registered resource: " + deviceResource);
}
registry.addResource(deviceResource);
log.fine("Registered resource: " + deviceResource);
}
log.fine("Adding item to registry with expiration in seconds: " + localDevice.getIdentity().getMaxAgeSeconds());
RegistryItem<UDN, LocalDevice> localItem = new RegistryItem<>(
localDevice.getIdentity().getUdn(),
localDevice,
localDevice.getIdentity().getMaxAgeSeconds()
);
getDeviceItems().add(localItem);
log.fine("Registered local device: " + localItem);
if (isByeByeBeforeFirstAlive(localItem.getKey()))
advertiseByebye(localDevice, true);
if (isAdvertised(localItem.getKey()))
advertiseAlive(localDevice);
for (final RegistryListener listener : registry.getListeners()) {
registry.getConfiguration().getRegistryListenerExecutor().execute(
new Runnable() {
public void run() {
listener.localDeviceAdded(registry, localDevice);
}
}
);
}
}
在add()方法中,根据传入的LocalDevice创建了对应的RegistryItem<UDN, LocalDevice>对象,然后将localItem添加到了Set<RegistryItem<UDN, D>>类型的deviceItems集合中;
根据之前getRegistry()的逻辑中,就提及到了advertiseAlive()方法的使用,但是之前还没有执行addDevice()方法,deviceItems集合为空,但是在addDevice()方法中再次调用了advertiseAlive(localDevice)方法,现在该方法中触发了root device、embedded device以及services list of the embedded device的device msg的sendMessage()逻辑;
至此,整个Server端UPnP设备宣告流程就执行完成了,接下来就是client端Control Point发现可用UPnP设备流程;
发现
mUpnpService = (AndroidUpnpService) service;
mUpnpService.getControlPoint().search();
同理,client的实现也需要绑定AndroidUpnpService,然后通过getControlPoint()获取AndroidUpnpService中的控制点实例,然后执行search()方法扫描局域网中可用的UPnP设备;
getControlPoint()
public ControlPoint getControlPoint() {
return upnpService.getControlPoint();
}
同理,upnpService对象为UpnpServiceImpl对象:
public ControlPoint getControlPoint() {
return controlPoint;
}
controlPoint实例的初始化同样也在UpnpServiceImpl的构造方法中:
public UpnpServiceImpl(UpnpServiceConfiguration configuration, RegistryListener... registryListeners) {
this.configuration = configuration;
this.protocolFactory = createProtocolFactory();
……………………
this.controlPoint = createControlPoint(protocolFactory, registry);
log.info("<<< UPnP service started successfully");
}
其中调用了createProtocolFactory()方法:
protected ProtocolFactory createProtocolFactory() {
return new ProtocolFactoryImpl(this);
}
创建了ProtocolFactoryImpl对象:
@Inject
public ProtocolFactoryImpl(UpnpService upnpService) {
log.fine("Creating ProtocolFactory: " + getClass().getName());
this.upnpService = upnpService;
}
紧接着执行了createControlPoint()方法:
protected ControlPoint createControlPoint(ProtocolFactory protocolFactory, Registry registry) {
return new ControlPointImpl(getConfiguration(), protocolFactory, registry);
}
我们看一下ControlPointImpl的构造方法:
@Inject
public ControlPointImpl(UpnpServiceConfiguration configuration, ProtocolFactory protocolFactory, Registry registry) {
log.fine("Creating ControlPoint: " + getClass().getName());
this.configuration = configuration;
this.protocolFactory = protocolFactory;
this.registry = registry;
}
至此,ControlPoint就创建成功;
在上述的过程中,我们多次看到了configuration,configuration这个变量的初始化逻辑在AndroidUpnpServiceImpl的onCreate()方法中:
upnpService = new UpnpServiceImpl(createConfiguration()) {
……………………
}
其中传入了createConfiguration()方法:
protected UpnpServiceConfiguration createConfiguration() {
return new AndroidUpnpServiceConfiguration();
}
创建了AndroidUpnpServiceConfiguration对象:
public AndroidUpnpServiceConfiguration() {
this(0); // Ephemeral port
}
public AndroidUpnpServiceConfiguration(int streamListenPort) {
super(streamListenPort, false);
// This should be the default on Android 2.1 but it's not set by default
System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver");
}
其中调用了super(),AndroidUpnpServiceConfiguration继承自DefaultUpnpServiceConfiguration:
protected DefaultUpnpServiceConfiguration(int streamListenPort, boolean checkRuntime) {
if (checkRuntime && ModelUtil.ANDROID_RUNTIME) {
throw new Error("Unsupported runtime environment, use org.fourthline.cling.android.AndroidUpnpServiceConfiguration");
}
this.streamListenPort = streamListenPort;
defaultExecutorService = createDefaultExecutorService();
datagramProcessor = createDatagramProcessor();
soapActionProcessor = createSOAPActionProcessor();
genaEventProcessor = createGENAEventProcessor();
deviceDescriptorBinderUDA10 = createDeviceDescriptorBinderUDA10();
serviceDescriptorBinderUDA10 = createServiceDescriptorBinderUDA10();
namespace = createNamespace();
}
在该构造方法中创建了许多关于协议传输过程中需要使用到的对象;
search() -- client发送search指令
public void search() {
search(new STAllHeader(), MXHeader.DEFAULT_VALUE);
}
public void search(UpnpHeader searchType, int mxSeconds) {
log.fine("Sending asynchronous search for: " + searchType.getString());
getConfiguration().getAsyncProtocolExecutor().execute(
getProtocolFactory().createSendingSearch(searchType, mxSeconds)
);
}
NotificationSubtype
在search()方法中,执行了new STAllHeader对象:
public class STAllHeader extends UpnpHeader<NotificationSubtype> {
public STAllHeader() {
setValue(NotificationSubtype.ALL);
}
public void setString(String s) throws InvalidHeaderException {
if (!s.equals(NotificationSubtype.ALL.getHeaderString())) {
throw new InvalidHeaderException("Invalid ST header value (not "+NotificationSubtype.ALL+"): " + s);
}
}
public String getString() {
return getValue().getHeaderString();
}
}
在STAllHeader的构造方法中调用了setValue(NotificationSubtype.ALL)方法,我们看一下setValue方法的逻辑:
public void setValue(T value) {
this.value = value;
}
value的类型为NotificationSubtype,所以我们看一下NotificationSubtype的定义:
public enum NotificationSubtype {
ALIVE("ssdp:alive"),
UPDATE("ssdp:update"),
BYEBYE("ssdp:byebye"),
ALL("ssdp:all"),
DISCOVER("ssdp:discover"),
PROPCHANGE("upnp:propchange");
private String headerString;
NotificationSubtype(String headerString) {
this.headerString = headerString;
}
public String getHeaderString() {
return headerString;
}
}
NotificationSubtype定义了发现设备的协议;
search()方法紧接着调用了getConfiguration().getAsyncProtocolExecutor(),其实就是在DefaultUpnpServiceConfiguration构造方法中初始化的defaultExecutorService变量,在defaultExecutorService变量的线程池中执行了getProtocolFactory().createSendingSearch(searchType, mxSeconds),getProtocolFactory()是ProtocolFactoryImpl对象,createSendingSearch的返回类型为Runnable:()
public SendingSearch createSendingSearch(UpnpHeader searchTarget, int mxSeconds) {
return new SendingSearch(getUpnpService(), searchTarget, mxSeconds);
}
其中创建了SendingSearch对象:
public SendingSearch(UpnpService upnpService, UpnpHeader searchTarget, int mxSeconds) {
super(upnpService);
if (!UpnpHeader.Type.ST.isValidHeaderType(searchTarget.getClass())) {
throw new IllegalArgumentException(
"Given search target instance is not a valid header class for type ST: " + searchTarget.getClass()
);
}
this.searchTarget = searchTarget;
this.mxSeconds = mxSeconds;
}
其中SendingSearch继承了SendingAsync,SendingAsync实现了Runnable:
public abstract class SendingAsync implements Runnable {
final private static Logger log = Logger.getLogger(UpnpService.class.getName());
private final UpnpService upnpService;
protected SendingAsync(UpnpService upnpService) {
this.upnpService = upnpService;
}
public UpnpService getUpnpService() {
return upnpService;
}
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
);
}
}
}
protected abstract void execute() throws RouterException;
@Override
public String toString() {
return "(" + getClass().getSimpleName() + ")";
}
}
SendingAsync的run()中执行了execute()方法,这个方法为abstract,在SendingSearch中实现:
protected void execute() throws RouterException {
log.fine("Executing search for target: " + searchTarget.getString() + " with MX seconds: " + getMxSeconds());
OutgoingSearchRequest msg = new OutgoingSearchRequest(searchTarget, getMxSeconds());
prepareOutgoingSearchRequest(msg);
for (int i = 0; i < getBulkRepeat(); i++) {
try {
getUpnpService().getRouter().send(msg);
// UDA 1.0 is silent about this but UDA 1.1 recommends "a few hundred milliseconds"
log.finer("Sleeping " + getBulkIntervalMilliseconds() + " milliseconds");
Thread.sleep(getBulkIntervalMilliseconds());
} catch (InterruptedException ex) {
// Interruption means we stop sending search messages, e.g. on shutdown of thread pool
break;
}
}
}
在execute()方法中创建了msg,然后调用getUpnpService().getRouter().send(msg)将msg发送到局域网中;
上述逻辑和UPnP设备宣告的逻辑非常类似;
msg的类型为OutgoingSearchRequest,OutgoingSearchRequest的构造方法:
public OutgoingSearchRequest(UpnpHeader searchTarget, int mxSeconds) {
super(
new UpnpRequest(UpnpRequest.Method.MSEARCH),
ModelUtil.getInetAddressByName(Constants.IPV4_UPNP_MULTICAST_GROUP),
Constants.UPNP_MULTICAST_PORT
);
this.searchTarget = searchTarget;
getHeaders().add(UpnpHeader.Type.MAN, new MANHeader(NotificationSubtype.DISCOVER.getHeaderString()));
getHeaders().add(UpnpHeader.Type.MX, new MXHeader(mxSeconds));
getHeaders().add(UpnpHeader.Type.ST, searchTarget);
getHeaders().add(UpnpHeader.Type.HOST, new HostHeader());
}
在该构造方法中,定义了UpnpRequest.Method,同时封装请求头数据;
Router为网络传输层接口,通过调用send()方法发送,Router的实现类为RouterImpl:
public void send(OutgoingDatagramMessage msg) throws RouterException {
lock(readLock);
try {
if (enabled) {
for (DatagramIO datagramIO : datagramIOs.values()) {
datagramIO.send(msg);
}
} else {
log.fine("Router disabled, not sending datagram: " + msg);
}
} finally {
unlock(readLock);
}
}
调用了datagramIO.send()方法:
synchronized public void send(OutgoingDatagramMessage message) {
if (log.isLoggable(Level.FINE)) {
log.fine("Sending message from address: " + localAddress);
}
DatagramPacket packet = datagramProcessor.write(message);
if (log.isLoggable(Level.FINE)) {
log.fine("Sending UDP datagram packet to: " + message.getDestinationAddress() + ":" + message.getDestinationPort());
}
send(packet);
}
write()
调用了datagramProcessor.write(message),datagramProcessor就是DefaultUpnpServiceConfiguration构造方法中初始化的变量:
public DatagramPacket write(OutgoingDatagramMessage message) throws UnsupportedDataException {
StringBuilder statusLine = new StringBuilder();
UpnpOperation operation = message.getOperation();
if (operation instanceof UpnpRequest) {
UpnpRequest requestOperation = (UpnpRequest) operation;
statusLine.append(requestOperation.getHttpMethodName()).append(" * ");
statusLine.append("HTTP/1.").append(operation.getHttpMinorVersion()).append("\r\n");
} else if (operation instanceof UpnpResponse) {
UpnpResponse responseOperation = (UpnpResponse) operation;
statusLine.append("HTTP/1.").append(operation.getHttpMinorVersion()).append(" ");
statusLine.append(responseOperation.getStatusCode()).append(" ").append(responseOperation.getStatusMessage());
statusLine.append("\r\n");
} else {
throw new UnsupportedDataException(
"Message operation is not request or response, don't know how to process: " + message
);
}
// UDA 1.0, 1.1.2: No body but message must have a blank line after header
StringBuilder messageData = new StringBuilder();
messageData.append(statusLine);
messageData.append(message.getHeaders().toString()).append("\r\n");
if (log.isLoggable(Level.FINER)) {
log.finer("Writing message data for: " + message);
log.finer("---------------------------------------------------------------------------------");
log.finer(messageData.toString().substring(0, messageData.length() - 2)); // Don't print the blank lines
log.finer("---------------------------------------------------------------------------------");
}
try {
// According to HTTP 1.0 RFC, headers and their values are US-ASCII
// TODO: Probably should look into escaping rules, too
byte[] data = messageData.toString().getBytes("US-ASCII");
log.fine("Writing new datagram packet with " + data.length + " bytes for: " + message);
return new DatagramPacket(data, data.length, message.getDestinationAddress(), message.getDestinationPort());
} catch (UnsupportedEncodingException ex) {
throw new UnsupportedDataException(
"Can't convert message content to US-ASCII: " + ex.getMessage(), ex, messageData
);
}
}
在该方法中对message进行解析,封装了对应格式的数据报包;
send()
最后调用了send()方法:
synchronized public void send(DatagramPacket datagram) {
if (log.isLoggable(Level.FINE)) {
log.fine("Sending message from address: " + localAddress);
}
try {
socket.send(datagram);
} catch (SocketException ex) {
log.fine("Socket closed, aborting datagram send to: " + datagram.getAddress());
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
log.log(Level.SEVERE, "Exception sending datagram to: " + datagram.getAddress() + ": " + ex, ex);
}
}
在该方法中使用了socket的send()方法发送了数据报包,socket的类型为DatagramSocket;
至此,通过多播套接字发送了发现广播;
需要注意的是,上述的两个send都是async,异步的,使用的是UDP的协议方式,之后的逻辑中都是使用TCP/IP的协议;
search_client响应search Response
client端发送了发现请求之后,等待响应。响应逻辑在之前描述的Router中定义了;
响应逻辑的定义在AsyncServletStreamServerImpl中:
protected Servlet createServlet(final Router router) {
return new HttpServlet() {
@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);
}
};
}
在该方法中调用了received()方法,用于处理接收到请求或者是响应;
在search()阶段,执行了SendingSearch中的execute()方法,用于发送search请求,UPnP设备在宣告阶段告知了局域网UPnP设备的存在,得到了NOTIFY类型的响应;
所以我们可以直接查看NOTIFY响应逻辑:
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);
}
调用了execute()方法执行了UpnpStream Runnable,所以我们看一下UpnpStream对象,该对象的类型为AsyncServletUpnpStream:
@Override
public void run() {
try {
StreamRequestMessage requestMessage = readRequestMessage();
if (log.isLoggable(Level.FINER))
log.finer("Processing new request message: " + requestMessage);
responseMessage = process(requestMessage);
……………………
} 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();
……………………
}
而在该方法中调用了syncProtocol.run(),syncProtocol对象类型为ReceivingAsync:
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) {
……………………
}
}
}
在该方法中调用了execute()方法,而execute()方法为抽象方法,该方法的实现有4种:ReceivingNotification、ReceivingSearch、ReceivingSearchResponse、ReceivingSync,我们需要区别一下ReceivingSearch和ReceivingSearchResponse:
- ReceivingSearch:处理搜索请求的接收,响应本地注册设备,这个处理在UPnP设备侧;
- ReceivingSearchResponse:处理搜索响应消息的接收,这个用于处理UPnP设备回应的response,在client侧;
而我们现在的阶段为search响应阶段,在client侧,所以直接看ReceivingSearchResponse中的execute():
public class ReceivingSearchResponse extends ReceivingAsync<IncomingSearchResponse> {
final private static Logger log = Logger.getLogger(ReceivingSearchResponse.class.getName());
public ReceivingSearchResponse(UpnpService upnpService, IncomingDatagramMessage<UpnpResponse> inputMessage) {
super(upnpService, new IncomingSearchResponse(inputMessage));
}
protected void execute() throws RouterException {
if (!getInputMessage().isSearchResponseMessage()) {
log.fine("Ignoring invalid search response message: " + getInputMessage());
return;
}
……………………
}
}
在该方法中调用了getInputMessage()方法来获取UPnP设备响应的数据,我们首先先分析一个InputMessage的赋值逻辑;
InputMessage变量
这个InputMessage的赋值逻辑在Router的enable()方法中,在enable()方法中调用了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) {
……………………
}
// Datagram I/O
DatagramIO datagramIO = getConfiguration().createDatagramIO(networkAddressFactory);
if (datagramIO == null) {
……………………
}
}
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()) {
if (log.isLoggable(Level.FINE))
log.fine("Starting datagram I/O on address: " + entry.getKey());
getConfiguration().getDatagramIOExecutor().execute(entry.getValue());
}
}
在startAddressBasedTransports()方法中创建了HTTP Server的响应和DatagramIO的响应,InputMessage就是通过调用createDatagramIO()方法的时候,创建了DatagramIO对象,然后在for循环中执行了getDatagramIOExecutor().execute(entry.getValue())方法,用于执行了DatagramIOImpl中的run()方法:
public void run() {
log.fine("Entering blocking receiving loop, listening for UDP datagrams on: " + socket.getLocalAddress());
while (true) {
try {
byte[] buf = new byte[getConfiguration().getMaxDatagramBytes()];
DatagramPacket datagram = new DatagramPacket(buf, buf.length);
socket.receive(datagram);
log.fine(
"UDP datagram received from: "
+ datagram.getAddress().getHostAddress()
+ ":" + datagram.getPort()
+ " on: " + localAddress
);
router.received(datagramProcessor.read(localAddress.getAddress(), datagram));
} catch (SocketException ex) {
log.fine("Socket closed");
break;
} catch (UnsupportedDataException ex) {
log.info("Could not read datagram: " + ex.getMessage());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
try {
if (!socket.isClosed()) {
log.fine("Closing unicast socket");
socket.close();
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
调用了DatagramIOImpl中的run(),其中有一个while无限循环,用于处理数据包的响应,其中调用了router.received(),received()方法的入参类型为IncomingDatagramMessage,即将响应消息封装成了IncomingDatagramMessage对象;
public void received(IncomingDatagramMessage msg) {
if (!enabled) {
log.fine("Router disabled, ignoring incoming message: " + msg);
return;
}
try {
ReceivingAsync protocol = getProtocolFactory().createReceivingAsync(msg);
if (protocol == null) {
if (log.isLoggable(Level.FINEST))
log.finest("No protocol, ignoring received message: " + msg);
return;
}
if (log.isLoggable(Level.FINE))
log.fine("Received asynchronous message: " + msg);
getConfiguration().getAsyncProtocolExecutor().execute(protocol);
} catch (ProtocolCreationException ex) {
log.warning("Handling received datagram failed - " + Exceptions.unwrap(ex).toString());
}
}
然后在该方法中调用了getProtocolFactory().createReceivingAsync(msg)方法:
public ReceivingAsync createReceivingAsync(IncomingDatagramMessage message) throws ProtocolCreationException {
if (log.isLoggable(Level.FINE)) {
log.fine("Creating protocol for incoming asynchronous: " + message);
}
if (message.getOperation() instanceof UpnpRequest) {
IncomingDatagramMessage<UpnpRequest> incomingRequest = message;
switch (incomingRequest.getOperation().getMethod()) {
case NOTIFY:
return isByeBye(incomingRequest) || isSupportedServiceAdvertisement(incomingRequest)
? createReceivingNotification(incomingRequest) : null;
case MSEARCH:
return createReceivingSearch(incomingRequest);
}
} else if (message.getOperation() instanceof UpnpResponse) {
IncomingDatagramMessage<UpnpResponse> incomingResponse = message;
return isSupportedServiceAdvertisement(incomingResponse)
? createReceivingSearchResponse(incomingResponse) : null;
}
throw new ProtocolCreationException("Protocol for incoming datagram message not found: " + message);
}
在该方法中通过判断message.getOperation()类型来创建对应不同的Receiver,我们目前值search的Response阶段,所以会调用createReceivingSearchResponse()方法:
protected ReceivingAsync createReceivingSearchResponse(IncomingDatagramMessage<UpnpResponse> incomingResponse) {
return new ReceivingSearchResponse(getUpnpService(), incomingResponse);
}
public ReceivingSearchResponse(UpnpService upnpService, IncomingDatagramMessage<UpnpResponse> inputMessage) {
super(upnpService, new IncomingSearchResponse(inputMessage));
}
protected ReceivingAsync(UpnpService upnpService, M inputMessage) {
this.upnpService = upnpService;
this.inputMessage = inputMessage;
}
public M getInputMessage() {
return inputMessage;
}
最后在一系列的透传下,进入了ReceivingAsync中,并将之间封装好的IncomingDatagramMessage对象赋值给了inputMessage;
创建RemoteDevice
public class ReceivingSearchResponse extends ReceivingAsync<IncomingSearchResponse> {
final private static Logger log = Logger.getLogger(ReceivingSearchResponse.class.getName());
public ReceivingSearchResponse(UpnpService upnpService, IncomingDatagramMessage<UpnpResponse> inputMessage) {
super(upnpService, new IncomingSearchResponse(inputMessage));
}
protected void execute() throws RouterException {
if (!getInputMessage().isSearchResponseMessage()) {
log.fine("Ignoring invalid search response message: " + getInputMessage());
return;
}
UDN udn = getInputMessage().getRootDeviceUDN();
if (udn == null) {
log.fine("Ignoring search response message without UDN: " + getInputMessage());
return;
}
RemoteDeviceIdentity rdIdentity = new RemoteDeviceIdentity(getInputMessage());
log.fine("Received device search response: " + rdIdentity);
if (getUpnpService().getRegistry().update(rdIdentity)) {
log.fine("Remote device was already known: " + udn);
return;
}
RemoteDevice rd;
try {
rd = new RemoteDevice(rdIdentity);
} catch (ValidationException ex) {
log.warning("Validation errors of device during discovery: " + rdIdentity);
for (ValidationError validationError : ex.getErrors()) {
log.warning(validationError.toString());
}
return;
}
if (rdIdentity.getDescriptorURL() == null) {
log.finer("Ignoring message without location URL header: " + getInputMessage());
return;
}
if (rdIdentity.getMaxAgeSeconds() == null) {
log.finer("Ignoring message without max-age header: " + getInputMessage());
return;
}
// Unfortunately, we always have to retrieve the descriptor because at this point we
// have no idea if it's a root or embedded device
getUpnpService().getConfiguration().getAsyncProtocolExecutor().execute(
new RetrieveRemoteDescriptors(getUpnpService(), rd)
);
}
}
InputMessage获取成功之后,就通过InputMessage来创建对应的RemoteDevice对象以及RemoteDevice相关的一些参数信息;
在最后执行了:
getUpnpService().getConfiguration().getAsyncProtocolExecutor().execute(
new RetrieveRemoteDescriptors(getUpnpService(), rd)
);
至此,宣告&发现阶段的工作就执行完成了。
接近着下来就是扫描阶段;