jdk.dio.DeviceManager Maven / Gradle / Ivy
Show all versions of org.openjdk.dio Show documentation
/*
* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.dio;
import java.io.IOException;
import java.security.AccessController;
import java.util.Iterator;
import java.util.Random;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import com.oracle.dio.impl.PeripheralDescriptorImpl;
import com.oracle.dio.impl.PeripheralFactory;
import com.oracle.dio.impl.Platform;
import com.oracle.dio.registry.RegistrationEventHandler;
import com.oracle.dio.registry.RegistrationEventSender;
import com.oracle.dio.registry.Registry;
import com.oracle.dio.utils.Constants;
import com.oracle.dio.utils.ExceptionMessage;
import com.oracle.dio.utils.Logging;
import com.oracle.dio.utils.PrivilegeController;
import com.oracle.dio.utils.PrivilegedAction;
import jdk.dio.spi.DeviceProvider;
import romizer.Local;
/**
* The {@code DeviceManager} class provides methods for opening and registering
* devices that can then be handled as {@link Device} instances. A device
* of a particular type can be opened using its platform-specific numeric ID or name as well as
* its properties or using an ad-hoc configuration (in which case, its hardware addressing
* information must be explicitly provided).
*
* A device may be identified by a numeric ID. This ID is unrelated to the
* hardware number (hardware addressing information) that may be used to identify a device such as a
* GPIO pin number or an I2C slave device address. A device ID typically corresponds to a
* registered configuration for a device. The numeric ID of a device must be
* greater than or equal to {@code 0} and must be unique. Yet the same device may be
* directly and indirectly mapped through several IDs; each ID may correspond to a different
* configuration, representation or abstraction for the same underlying device hardware
* resource.
* A device opened with an ad-hoc configuration - that is: not
* through one of its registered configurations - is not assigned a numeric ID nor a name. Its
* numeric ID and name are both undefined and set respectively to
* {@link DeviceDescriptor#UNDEFINED_ID UNDEFINED_ID} and {@code null}.
*
* Devices may be opened in either
* exclusive or shared mode. By default,
* devices are opened in exclusive mode. Whether a device can be opened in
* shared mode depends on the underlying device hardware as well as on the underlying
* device driver. It also depends on whether the provided {@code Device} implementation is a
* dedicated, virtualized or shared abstraction of the underlying
* device resource.
* When a device is open with an ad-hoc configuration in shared mode then the
* {@code Device} implementation (or driver) may throw a
* {@link InvalidDeviceConfigException} if the device is already open and the
* requested adhoc configuration is incompatible with the current configuration of the
* device.
* When a device is open in shared mode then some explicit means of access
* synchronization may have to be used such as by invoking {@link Device#tryLock Device.tryLock} and
* {@link Device#unlock Device.unlock}. Device locks are held on a per {@code Device} instance basis.
* When the same device is open twice in shared access mode by the same application,
* locking one of the {@code Device} instances will prevent the other from being accessed/used.
*
* Opening a device of a specific type with a registered configuration is
* subject to permission checks (see {@link DeviceMgmtPermission#OPEN}).
* Opening a device of a specific type with an ad-hoc configuration is subject
* to permission checks specific for that type (for example see
* {@link jdk.dio.gpio.GPIOPinPermission GPIOPinPermission.OPEN}). This permission check
* should be implemented by the {@link jdk.dio.spi.DeviceProvider#open
* DeviceProvider.open} method.
* Registration and unregistration of devices are subject to permission checks (see
* {@link DeviceMgmtPermission#REGISTER} and {@link DeviceMgmtPermission#UNREGISTER}).
*
* For more details see Security Model.
*
* Device Probing
* For some peripheral hardware such as I2C bus or SPI bus, opening (or registering) a device
* such as a slave device on a bus does not necessarily entail immediately
* accessing the device. Upon opening the device the underlying platform or
* driver may probe for the device to check whether the device exists and is
* addressable and whether the requested configuration is valid; if the probing
* for device determines that the device does not exist or is not addressable
* or, that it does not support the requested configuration a
* {@code DeviceNotFoundException} or respectively an
* {@code InvalidDeviceConfigException} is thrown. When the underlying platform or
* driver does not implement any probing facility a {@code Device} instance
* is returned, but I/O operations on the device will later fail - typically with an
* {@code IOException} - if the device does not exist, is not addressable, or
* does not support the requested configuration.
*
* @see UnavailableDeviceException
* @see DeviceMgmtPermission
* @see Device
* @see jdk.dio.spi.DeviceProvider
* @see DeviceConfig
* @since 1.0
*/
@apimarker.API("device-io_1.1")
public class DeviceManager {
/**
* Exclusive access mode.
*
* This bit flag can be bitwise-combined (OR) with other access mode bit flags.
*
*/
public static final int EXCLUSIVE = 1;
/**
* Shared access mode.
*
* This bit flag can be bitwise-combined (OR) with other access mode bit flags.
*
*/
public static final int SHARED = 2;
/**
* Unspecified device numeric ID - requesting the allocation of a free ID.
*
* @see #register register
*/
public static final int UNSPECIFIED_ID = -1;
/**
* Platform specific initialization.
*/
static {
Platform.initialize();
}
/**
* List all platform- and user-registered devices.
*
* @param
* the type of devices listed.
* @return an enumeration of the descriptors of all registered devices.
*/
public static
> Iterator> list() {
return Registry.getInstance().list(null);
}
/**
* List all platform- and user-registered devices of the designated type.
*
* @param
* the type of devices to list.
* @param intf
* the interface (sub-interface of {@code Device}) of the device to be
* registered.
* @return an enumeration of the descriptors of all registered devices of the designated
* type.
* @throws NullPointerException
* if {@code intf} is {@code null}.
*/
public static
> Iterator> list(Class intf) {
// checks for null
intf.isArray();
return Registry.getInstance().list(intf);
}
/**
* Opens a device, returning a {@code Device} instance of the specified type to access it.
* The device to open is designated by the provided hardware addressing information and is
* initially set-up according to the specified configuration. The specified device type and specified
* configuration type must be compatible.
*
* Opening a device from its hardware addressing information and with an ad-hoc configuration
* may be subject to device probing limitations .
*
* The device is opened in exclusive access mode.
*
* The returned {@code Device} instance's ID and name are undefined.
* A new instance is returned upon each call.
*
*
* @param
* the type of the device to open.
* @param intf
* the interface (sub-interface of {@code Device}) of the device to open.
* @param config
* the device configuration (which includes hardware addressing information as
* well as configuration parameters).
* @return a new {@code Device} instance to access the designated device.
* @throws UnsupportedDeviceTypeException
* if the designated device type is not supported.
* @throws InvalidDeviceConfigException
* if the provided device configuration (as defined by the configuration
* parameters) is not valid/supported.
* @throws DeviceNotFoundException
* if the device designated by the hardware addressing information is not found.
* @throws UnavailableDeviceException
* if the designated device is not currently available - such as if it is
* already open with exclusive access.
* @throws IOException
* if any other I/O error occurred.
* @throws SecurityException
* if the caller has no permission to access the designated device (see
* {@link DevicePermission#OPEN}).
* @throws NullPointerException
* if {@code intf} or {@code config} is {@code null}.
* @throws ClassCastException
* if the device configuration type specified by {@code config}
* is not applicable to the device type specified by {@code intf}.
*/
public static
> P open(Class
intf, DeviceConfig super P> config)
throws IOException, InvalidDeviceConfigException, UnsupportedDeviceTypeException,
DeviceNotFoundException, UnavailableDeviceException {
return open(intf, config, EXCLUSIVE);
}
/**
* Opens a device, returning a {@code Device} instance of the specified type to access it.
* The device to open is designated by the provided hardware addressing information and is
* initially set-up according to the specified configuration. The specified device type and specified
* configuration type must be compatible.
*
* Opening a device from its hardware addressing information and with an ad-hoc configuration
* may be subject to device probing limitations .
*
* The device is opened in the designated access mode. A device may be
* opened in shared mode if supported by the underlying driver and hardware and if it is not
* already opened in exclusive mode. A device may be opened in exclusive mode if
* supported by the underlying driver and hardware and if it is not already opened.
*
* The returned {@code Device} instance's ID and name are undefined.
* A new instance is returned upon each call.
*
*
* @param
* the type of the device to open.
* @param intf
* the interface (sub-interface of {@code Device}) of the device to open.
* @param config
* the device configuration (which includes hardware addressing information as
* well as configuration parameters).
* @param mode
* the access mode, one of: {@link #EXCLUSIVE} or {@link #SHARED}.
* @return a new {@code Device} instance to access the designated device.
* @throws UnsupportedDeviceTypeException
* if the designated device type is not supported.
* @throws InvalidDeviceConfigException
* if the provided device configuration (as defined by the configuration
* parameters) is not valid/supported or when opened in shared mode, if the provided
* device configuration is incompatible with the currently open configuration of
* the device.
* @throws DeviceNotFoundException
* if the device designated by the hardware addressing information is not found.
* @throws UnavailableDeviceException
* if the designated device is not currently available - such as if it is
* already open with exclusive access.
* @throws UnsupportedAccessModeException
* if the requested access mode is not supported.
* @throws IOException
* if any other I/O error occurred.
* @throws SecurityException
* if the caller has no permission to access the designated device (see
* {@link DevicePermission#OPEN}).
* @throws NullPointerException
* if {@code intf} or {@code config} is {@code null}.
* @throws ClassCastException
* if the device configuration type specified by {@code config}
* is not applicable to the device type specified by {@code intf}.
*/
public static
> P open(Class
intf, DeviceConfig super P> config, int mode)
throws IOException, InvalidDeviceConfigException, UnsupportedDeviceTypeException,
DeviceNotFoundException, UnavailableDeviceException, UnsupportedAccessModeException {
if (null == intf) {
throw new NullPointerException(
ExceptionMessage.format(ExceptionMessage.DEVICE_NULL_CONFIG_OR_INTF)
);
}
return openWithConfig(intf, config, mode);
}
private static
> P openWithConfig(Class
intf, DeviceConfig super P> config, int mode)
throws IOException, InvalidDeviceConfigException, UnsupportedDeviceTypeException,
DeviceNotFoundException, UnavailableDeviceException, UnsupportedAccessModeException {
if (null == config) {
throw new NullPointerException(
ExceptionMessage.format(ExceptionMessage.DEVICE_NULL_CONFIG_OR_INTF)
);
}
checkMode(mode);
PeripheralDescriptorImpl
descr = new PeripheralDescriptorImpl(UNSPECIFIED_ID, null, config, intf, null);
if (null != intf) {
try {
return ((PeripheralFactory
)getFactory(intf)).create(descr, mode);
} catch (DeviceNotFoundException | UnsupportedDeviceTypeException e) {
P res = (P)loadFromDriver(descr, mode);
if (null == res) {
throw e;
}
return res;
}
} else {
// special case: getDefaultType returns null that means config is not for embedded drivers
// try to load from installed drivers
P res = (P)loadFromDriver(descr, mode);
if (null == res) {
throw new UnsupportedDeviceTypeException(config.toString());
}
return res;
}
}
/**
* Looks up then opens the device designated by the provided numeric ID,
* returning a {@code Device} instance to access it.
* The device configuration registered for the provided ID is first looked up;
* the device designated by the retrieved hardware addressing information is open and is
* initially set-up according to the registered configuration.
*
* The device is opened in exclusive access mode.
* A new instance is returned upon each call.
*
*
* @param
* the type of the device to open.
* @param id
* the numeric device id.
* @return a new {@code Device} instance to access the designated device.
* @throws DeviceNotFoundException
* if the designated device is not found.
* @throws UnavailableDeviceException
* if the designated device is not currently available - such as if it is
* already open with exclusive access.
* @throws IOException
* if any other I/O error occurred.
* @throws SecurityException
* if the caller has no permission to access the designated device (see
* {@link DeviceMgmtPermission#OPEN}).
* @throws IllegalArgumentException
* if {@code id} is less than {@code 0}.
*/
public static
> P open(int id) throws IOException, DeviceNotFoundException,
UnavailableDeviceException {
try {
return (P)open(id, Device.class);
} catch (UnsupportedDeviceTypeException e) {
throw new DeviceNotFoundException(
ExceptionMessage.format(ExceptionMessage.DEVICE_CONFIG_PROBLEM, e.getMessage())
);
}
}
/**
* Looks up then opens the device designated by the provided numeric ID and type,
* returning a {@code Device} instance of the specified type to access it.
* The device configuration registered for the provided ID is first looked up;
* the device designated by the retrieved hardware addressing information is open and is
* initially set-up according to the registered configuration. The specified device type and retrieved
* configuration type must be compatible.
*
* The device is opened in exclusive access mode.
* A new instance is returned upon each call.
*
*
* @param
* the type of the device to open.
* @param id
* the numeric device id.
* @param intf
* the interface (sub-interface of {@code Device}) of the device being looked
* up.
* @return a new {@code Device} instance to access the designated device.
* @throws DeviceNotFoundException
* if the designated device is not found.
* @throws UnsupportedDeviceTypeException
* if the designated device type is not supported.
* @throws UnavailableDeviceException
* if the designated device is not currently available - such as if it is
* already open with exclusive access.
* @throws IOException
* if any other I/O error occurred.
* @throws SecurityException
* if the caller has no permission to access the designated device (see
* {@link DeviceMgmtPermission#OPEN}).
* @throws IllegalArgumentException
* if {@code id} is less than {@code 0}.
* @throws NullPointerException
* if {@code intf} is {@code null}.
*/
public static
> P open(int id, Class
intf) throws IOException,
UnsupportedDeviceTypeException, DeviceNotFoundException, UnavailableDeviceException {
try {
return open(id, intf, EXCLUSIVE);
} catch (UnsupportedAccessModeException e) {
throw new DeviceNotFoundException(
ExceptionMessage.format(ExceptionMessage.DEVICE_EXCLUSIVE_MODE_UNSUPPORTED)
);
}
}
/**
* Looks up then opens the device designated by the provided numeric ID and type,
* returning a {@code Device} instance of the specified type to access it.
* The device configuration registered for the provided ID is first looked up;
* the device designated by the retrieved hardware addressing information is open and is
* initially set-up according to the registered configuration. The specified device type and retrieved
* configuration type must be compatible.
*
* The device is opened in the designated access mode. A device may be
* opened in shared mode if supported by the underlying driver and hardware and if it is not
* already opened in exclusive mode. A device may be opened in exclusive mode if
* supported by the underlying driver and hardware and if it is not already opened.
*
* A new instance is returned upon each call.
*
*
* @param
* the type of the device to open.
* @param id
* the numeric device id.
* @param intf
* the interface (sub-interface of {@code Device}) of the device being looked
* up.
* @param mode
* the access mode, one of: {@link #EXCLUSIVE} or {@link #SHARED}.
* @return a new {@code Device} instance to access the designated device.
* @throws UnsupportedDeviceTypeException
* if the designated device type is not supported.
* @throws DeviceNotFoundException
* if the designated device is not found.
* @throws UnavailableDeviceException
* if the designated device is not currently available - such as when it is
* already open in an access mode incompatible with the requested access mode.
* @throws UnsupportedAccessModeException
* if the requested access mode is not supported.
* @throws IOException
* if any other I/O error occurred.
* @throws SecurityException
* if the caller has no permission to access the designated device (see
* {@link DeviceMgmtPermission#OPEN}).
* @throws IllegalArgumentException
* if {@code id} is less than {@code 0}.
* @throws NullPointerException
* if {@code intf} is {@code null}.
*/
public static
> P open(int id, Class
intf, int mode) throws IOException,
UnsupportedDeviceTypeException, DeviceNotFoundException, UnavailableDeviceException,
UnsupportedAccessModeException {
Registry.checkID(id);
do {
AccessController.checkPermission(new DeviceMgmtPermission("*:" + id, DeviceMgmtPermission.OPEN));
} while (false);
checkMode(mode);
if (null == intf) {
throw new NullPointerException(
ExceptionMessage.format(ExceptionMessage.DEVICE_NULL_INTF)
);
}
PeripheralDescriptorImpl
descr = (PeripheralDescriptorImpl
)Registry.getInstance().get(id);
if (null == descr) {
throw new DeviceNotFoundException(
ExceptionMessage.format(ExceptionMessage.DEVICE_NOT_FOUND, String.valueOf(id))
);
}
if (!intf.isAssignableFrom(descr.getInterface())) {
getFactory(intf);
throw new DeviceNotFoundException(
ExceptionMessage.format(ExceptionMessage.DEVICE_HAS_DIFFERENT_TYPE, id, descr.getInterface())
);
}
try {
final PeripheralFactory
f = getFactory(descr.getInterface());
final DeviceDescriptor
fdescr = descr;
final int fmode = mode;
return PrivilegeController.doPrivileged(new PrivilegedAction
() {
public P run() throws IOException {
return f.create(fdescr, fmode);
}
});
} catch (InvalidDeviceConfigException e) {
throw new DeviceNotFoundException(e.getMessage());
} catch (DeviceNotFoundException | UnsupportedDeviceTypeException e) {
P res = (P)loadFromDriver(descr, mode);
if (null == res) {
throw e;
}
return res;
}
}
/**
* Looks up then opens the device designated by the provided numeric ID,
* returning a {@code Device} instance to access it.
* The device configuration registered for the provided ID is first looked up;
* the device designated by the retrieved hardware addressing information is open and is
* initially set-up according to the registered configuration.
*
* The device is opened in the designated access mode. A device may be
* opened in shared mode if supported by the underlying driver and hardware and if it is not
* already opened in exclusive mode. A device may be opened in exclusive mode if
* supported by the underlying driver and hardware and if it is not already opened.
*
* The device is opened in exclusive access mode.
* A new instance is returned upon each call.
*
*
* @param
* the type of the device to open.
* @param id
* the numeric device id.
* @param mode
* the access mode, one of: {@link #EXCLUSIVE} or {@link #SHARED}.
* @return a new {@code Device} instance to access the designated device.
* @throws DeviceNotFoundException
* if the designated device is not found.
* @throws UnavailableDeviceException
* if the designated device is not currently available - such as when it is
* already open in an access mode incompatible with the requested access mode.
* @throws UnsupportedAccessModeException
* if the requested access mode is not supported.
* @throws IOException
* if any other I/O error occurred.
* @throws SecurityException
* if the caller has no permission to access the designated device (see
* {@link DeviceMgmtPermission#OPEN}).
* @throws IllegalArgumentException
* if {@code id} is less than {@code 0}.
*/
public static
> P open(int id, int mode) throws IOException, DeviceNotFoundException,
UnavailableDeviceException, UnsupportedAccessModeException {
try {
return (P)open(id, Device.class, mode);
} catch (UnsupportedDeviceTypeException e) {
throw new DeviceNotFoundException(
ExceptionMessage.format(ExceptionMessage.DEVICE_CONFIG_PROBLEM, e.getMessage())
);
}
}
/**
* Opens a device, returning a {@code Device} instance of the specified type to access it.
* The device to open is designated by the provided hardware addressing information and is
* initially set-up according to the specified configuration.
* The type of the device is inferred from the configuration type.
*
* Opening a device from its hardware addressing information and with an ad-hoc configuration
* may be subject to device probing limitations .
*
* The device is opened in exclusive access mode.
*
* The returned {@code Device} instance's ID and name are undefined.
* A new instance is returned upon each call.
*
*
* @param
* the type of the device to open.
* @param config
* the device configuration (which includes hardware addressing information as
* well as configuration parameters).
* @return a new {@code Device} instance to access the designated device.
* @throws UnsupportedDeviceTypeException
* if the designated device type is not supported.
* @throws InvalidDeviceConfigException
* if the provided device configuration (as defined by the configuration
* parameters) is not valid/supported.
* @throws DeviceNotFoundException
* if the device designated by the hardware addressing information is not found.
* @throws UnavailableDeviceException
* if the designated device is not currently available - such as if it is
* already open with exclusive access.
* @throws IOException
* if any other I/O error occurred.
* @throws SecurityException
* if the caller has no permission to access the designated device (see
* {@link DevicePermission#OPEN}).
* @throws NullPointerException
* if {@code config} is {@code null}.
*/
public static
> P open(DeviceConfig super P> config) throws IOException,
InvalidDeviceConfigException, UnsupportedDeviceTypeException, DeviceNotFoundException,
UnavailableDeviceException {
return open(config, EXCLUSIVE);
}
/**
* Opens a device, returning a {@code Device} instance of the specified type to access it.
* The device to open is designated by the provided hardware addressing information and is
* initially set-up according to the specified configuration.
* The type of the device is inferred from the configuration type.
*
* Opening a device from its hardware addressing information and with an ad-hoc configuration
* may be subject to device probing limitations.
*
* The device is opened in the designated access mode. A device may be
* opened in shared mode if supported by the underlying driver and hardware and if it is not
* already opened in exclusive mode. A device may be opened in exclusive mode if
* supported by the underlying driver and hardware and if it is not already opened.
*
* The returned {@code Device} instance's ID and name are undefined.
* A new instance is returned upon each call.
*
*
* @param
* the type of the device to open.
* @param config
* the device configuration (which includes hardware addressing information as
* well as configuration parameters).
* @param mode
* the access mode, one of: {@link #EXCLUSIVE} or {@link #SHARED}.
* @return a new {@code Device} instance to access the designated device.
* @throws UnsupportedDeviceTypeException
* if the designated device type is not supported.
* @throws InvalidDeviceConfigException
* if the provided device configuration (as defined by the configuration
* parameters) is not valid/supported or when opened in shared mode, if the provided
* device configuration is incompatible with the currently open configuration of
* the device.
* @throws DeviceNotFoundException
* if the device designated by the hardware addressing information is not found.
* @throws UnavailableDeviceException
* if the designated device is not currently available - such as if it is
* already open with exclusive access.
* @throws UnsupportedAccessModeException
* if the requested access mode is not supported.
* @throws IOException
* if any other I/O error occurred.
* @throws SecurityException
* if the caller has no permission to access the designated device (see
* {@link DevicePermission#OPEN}).
* @throws NullPointerException
* if {@code config} is {@code null}.
*/
public static
> P open(DeviceConfig super P> config, int mode) throws IOException,
InvalidDeviceConfigException, UnsupportedDeviceTypeException, DeviceNotFoundException,
UnavailableDeviceException, UnsupportedAccessModeException {
return openWithConfig(getDefaultType(config),config,mode);
}
/**
* Looks up then opens a device designated by the provided name, type and properties,
* returning a {@code Device} instance of the specified type to access it.
* A registered device configuration matching the provided name, type and properties is first looked up;
* if the device designated by the retrieved hardware addressing information is available
* it is open and initially set-up according to the matching configuration;
* if the device is already open in a mode that is not compatible
* with the requested mode the next matching registered device configuration is considered.
*
* A provided {@code null} name matches all registered device names; an empty
* string name can only be matched by an empty string name or a by a {@code null} name.
*
* The device is opened in the designated access mode. A device may be
* opened in shared mode if supported by the underlying driver and hardware and if it is not
* already opened in exclusive mode. A device may be opened in exclusive mode if
* supported by the underlying driver and hardware and if it is not already opened.
*
* A new instance is returned upon each call.
*
* Property-based lookup only uses exact (case-insensitive) matching and does not perform any
* semantic interpretation.
*
*
* @param
* the type of the device to open.
* @param name
* the device name; may be {@code null}.
* @param intf
* the interface (sub-interface of {@code Device}) of the device being looked
* up.
* @param mode
* the access mode, one of: {@link #EXCLUSIVE} or {@link #SHARED}.
* @param properties
* the list of required properties; may be {@code null}.
* @return a new {@code Device} instance to access the designated device.
* @throws UnsupportedDeviceTypeException
* if the designated device type is not supported.
* @throws DeviceNotFoundException
* if the designated device is not found.
* @throws UnavailableDeviceException
* if the designated device is not currently available - such as when it is
* already open in an access mode incompatible with the requested access mode.
* @throws UnsupportedAccessModeException
* if the requested access mode is not supported.
* @throws IOException
* if any other I/O error occurred.
* @throws SecurityException
* if the caller has no permission to access the designated device (see
* {@link DeviceMgmtPermission#OPEN}).
* @throws IllegalArgumentException
* if both {@code name} is {@code null} and {@code properties} is empty.
* @throws NullPointerException
* if {@code intf} is {@code null}.
*/
public static
> P open(String name, Class
intf, int mode, String... properties)
throws IOException, UnsupportedDeviceTypeException, DeviceNotFoundException,
UnavailableDeviceException, UnsupportedAccessModeException {
if (null == name && (null == properties || 0 == properties.length)) {
throw new IllegalArgumentException(
ExceptionMessage.format(ExceptionMessage.DEVICE_NULL_NAME_AND_PROPERTIES)
);
}
do {
AccessController.checkPermission(new DeviceMgmtPermission(((null == name)? "" : name) + ":*", DeviceMgmtPermission.OPEN));
} while (false);
if (null == intf) {
throw new NullPointerException(
ExceptionMessage.format(ExceptionMessage.DEVICE_NULL_INTF)
);
}
checkMode(mode);
// try to guess if this device type is valid
try {
getFactory(intf);
} catch (UnsupportedDeviceTypeException e) {
// nothing found yet
checkWithProviders(intf);
}
Iterator> iter = Registry.getInstance().get(name, intf, properties);
while (iter.hasNext()) {
PeripheralDescriptorImpl descr = iter.next();
try {
final PeripheralFactory
f = getFactory(descr.getInterface());
final DeviceDescriptor
fdescr = descr;
final int fmode = mode;
return PrivilegeController.doPrivileged(new PrivilegedAction
() {
public P run() throws IOException {
return f.create(fdescr, fmode);
}
});
} catch (InvalidDeviceConfigException e) {
throw new DeviceNotFoundException(e.getMessage());
} catch (UnavailableDeviceException e2) {
if (iter.hasNext()) {
// find next configuration
continue;
}
throw e2;
} catch (DeviceNotFoundException | UnsupportedDeviceTypeException e) {
P res = (P)loadFromDriver(descr, mode);
if (null == res) {
throw e;
}
return res;
}
}
// type is valid, but no valid config is found
throw new DeviceNotFoundException(name);
}
/**
* Looks up then opens a device designated by the provided name, type and properties,
* returning a {@code Device} instance of the specified type to access it.
* A registered device configuration matching the provided name, type and properties is first looked up;
* if the device designated by the retrieved hardware addressing information is available
* it is open and initially set-up according to the matching configuration;
* if the device is already open (therefore not available)
* the next matching registered device configuration is considered.
*
* A provided {@code null} name matches all registered device names; an empty
* string name can only be matched by an empty string name or a by a {@code null} name.
*
* The device is opened in exclusive access mode.
*
* A new instance is returned upon each call.
*
* Property-based lookup only uses exact (case-insensitive) matching and does not perform any
* semantic interpretation.
*
*
* @param
* the type of the device to open.
* @param name
* the device name; may be {@code null}.
* @param intf
* the interface (sub-interface of {@code Device}) of the device being looked
* up.
* @param properties
* the list of required properties; may be {@code null}.
* @return a new {@code Device} instance to access the designated device.
* @throws UnsupportedDeviceTypeException
* if the designated device type is not supported.
* @throws DeviceNotFoundException
* if the designated device is not found.
* @throws UnavailableDeviceException
* if the designated device is not currently available - such as if it is
* already open with exclusive access.
* @throws IOException
* if any other I/O error occurred.
* @throws SecurityException
* if the caller has no permission to access the designated device (see
* {@link DeviceMgmtPermission#OPEN}).
* @throws IllegalArgumentException
* if both {@code name} is {@code null} and {@code properties} is empty.
* @throws NullPointerException
* if {@code intf} is {@code null}.
*/
public static
> P open(String name, Class
intf, String... properties) throws IOException,
UnsupportedDeviceTypeException, DeviceNotFoundException, UnavailableDeviceException {
try {
return open(name, intf, EXCLUSIVE, properties);
} catch (UnsupportedAccessModeException e) {
throw new DeviceNotFoundException(
ExceptionMessage.format(ExceptionMessage.DEVICE_EXCLUSIVE_MODE_UNSUPPORTED)
);
}
}
/**
* Registers under the specified ID and name (as well as optional properties) a new device
* supporting the provided configuration. Upon successful registration all
* {@link RegistrationListener} instances registered for the type of the registered device
* are notified.
*
* An implementation of the {@code DeviceManager} MUST guarantee that an application
* registering a device is the first one to get notified (in the event it has registered a
* {@code RegistrationListener} for that type of devices).
*
* The designated device may be probed to check if the provided configuration is valid
* (see device probing limitations ).
*
* Prior to registering a new device of a certain type the
* {@link DeviceMgmtPermission} is checked with the action
* {@link DeviceMgmtPermission#REGISTER DeviceMgmtPermission.REGISTER}.
* For example, if a device of type {@link jdk.dio.gpio.GPIOPin
* GPIOPin} is to be registered the {@code DeviceMgmtPermission} is checked with a target
* name composed of the requested device name and ID and with the action
* {@link DeviceMgmtPermission#REGISTER DeviceMgmtPermission.REGISTER}.
*
* The following is an example of how this method may be used to register a new UART with Modem
* control lines:
*
* DeviceManager.register(10, // the device ID
* ModemUART.class, // the device type/interface
* new UARTConfig.Builder()
* .setControllerNumber(0)
* .setChannelNumber(0)
* .setBaudRate(2400)
* .setDataBits(UARTConfig.DATABITS_8)
* .setParity(UARTConfig.PARITY_EVEN)
* .setStopBits(UARTConfig.STOPBITS_1)
* .setFlowControlMode(UARTConfig.FLOWCONTROL_NONE)
* .build(), // the device configuration
* "MODEM", // the device name
* "com.foobar.modem.xxx=true", "com.foobar.modem.yyy=true" // the device capabilities
* );
*
*
*
* @param
* the type of the device to be registered.
* @param id
* the device ID; if {@code id} is equal to {@link #UNSPECIFIED_ID} a free ID
* will be allocated.
* @param intf
* the interface (sub-interface of {@code Device}) of the device to be
* registered.
* @param config
* the device configuration.
* @param name
* the name of the device to be registered.
* @param properties
* the list of properties/capabilities of the device to be registered; may be
* {@code null}.
* @return the assigned device ID.
* @throws InvalidDeviceConfigException
* if the provided device configuration (as defined by the configuration
* parameters) is not valid/supported.
* @throws UnsupportedDeviceTypeException
* if the designated device type is not supported.
* @throws DeviceNotFoundException
* if the device designated by the hardware addressing information is not found.
* @throws DeviceAlreadyExistsException
* if {@code id} is already assigned to a device.
* @throws IOException
* if any other I/O error occurred.
* @throws NullPointerException
* if {@code name}, {@code intf} or {@code config} is {@code null}.
* @throws IllegalArgumentException
* if {@code id} is less than {@code 0} and is not equal to {@link #UNSPECIFIED_ID}.
* @throws UnsupportedOperationException
* if registering a new device is not supported - such as may be the case
* on a platform supporting only a closed device topology.
* @throws SecurityException
* if the caller does not have the required permission (see
* {@link DeviceMgmtPermission#REGISTER}).
* @throws ClassCastException
* if the device configuration type specified by {@code config}
* is not applicable to the device type specified by {@code intf}.
*/
public static
> int register(int id, Class
intf, DeviceConfig super P> config,
String name, String... properties) throws IOException, UnsupportedDeviceTypeException, InvalidDeviceConfigException,
DeviceNotFoundException, DeviceAlreadyExistsException {
// quick fix: need info about custom DeviceConfig factory (if any)
PeripheralDescriptorImpl
dscr = new PeripheralDescriptorImpl(id, name, config, intf, properties);
// this fills dscr with correct provider
try (Device d = loadFromDriver(dscr, EXCLUSIVE)) {
} catch (Exception e) {
// intentionally ignored
}
int new_id = Registry.getInstance().register(dscr);
RegistrationEventSender.notifyRegistered(null, dscr);
return new_id;
}
/**
* Unregisters the device associated with the specified ID. Upon successful
* unregistration all {@link RegistrationListener} instances registered for the type of the
* device that has been unregistered are notified.
*
* Some devices are registered by the underlying platform and cannot be unregistered.
*
* Unregistration of a device has no side effect on its currently open
* {@code Device} instances. These {@code Device} instances especially retain the
* device ID that was assigned to them at the time they were open.
*
* This method returns silently if the provided ID does not correspond to a registered
* device.
*
*
* @param id
* the ID of the device to unregister.
* @throws IllegalArgumentException
* if {@code id} is less than {@code 0} or if {@code id} corresponds to a device
* device registered by the platform.
* @throws SecurityException
* if the caller does not have the required permission (see
* {@link DeviceMgmtPermission#UNREGISTER}).
*/
public static void unregister(int id) {
final Registry r = Registry.getInstance();
DeviceDescriptor unreg_d = r.unregister(id);
// send notify
if (null != unreg_d)
RegistrationEventSender.notifyUnregistered(null, unreg_d);
}
/**
* Adds the specified registration listener to receive notification of registration and
* unregistration of devices of the specified type.
*
* @param
* the type of the device to be listened for.
* @param listener
* the registration listener.
* @param intf
* the interface (sub-interface of {@code Device}) of the devices to be
* listened for.
* @throws NullPointerException
* if {@code listener} or {@code intf} is {@code null}.
*/
public static
> void addRegistrationListener(RegistrationListener
listener, Class
intf) {
// checks for null
listener.getClass();
intf.isArray();
RegistrationEventHandler.addListener(listener, intf);
}
/**
* Removes the specified registration listener so that it no longer receives notification of
* registration and unregistration of devices of the specified type.
*
* @param
* the type of the device listened for.
* @param listener
* the registration listener.
* @param intf
* the interface (sub-interface of {@code Device}) of the devices listened
* for.
* @throws NullPointerException
* if {@code listener} or {@code intf} is {@code null}.
*/
public static
> void removeRegistrationListener(RegistrationListener
listener, Class
intf) {
// checks for null
listener.getClass();
intf.isArray();
RegistrationEventHandler.removeListener(listener, intf);
}
/**
* Prevents instantiation.
*/
private DeviceManager() {}
/* ------------------- Private API ---------------- */
private static void checkMode(int mode) throws UnsupportedAccessModeException {
if (SHARED != mode && EXCLUSIVE != mode) {
throw new UnsupportedAccessModeException();
}
}
@Local(WeakDontRenameSubtypes = {"jdk.dio.DeviceConfig"})
private static
> Class
getDefaultType(DeviceConfig super P> config) throws UnsupportedDeviceTypeException {
String fullName = config.getClass().getName();
if (-1 != fullName.indexOf(Constants.PREFIX)) {
try {
// extract peripheral name from config name
// i.e. ADCChannel from com.oracle.dio.ADCChannelConfig
int configPos = fullName.indexOf(Constants.CONFIG);
return (Class
)Class.forName(fullName.substring(0, configPos));
} catch (RuntimeException | ClassNotFoundException e) {
}
}
// this will cause NPE at open(Class, DeviceConfig, int)
// fix later
return null;
}
@Local(DontRenameNonAbstractSubtypes = {"com.oracle.dio.impl.PeripheralFactory"})
private static PeripheralFactory getFactory(Class clazz) throws UnsupportedDeviceTypeException {
// get package name of com.oracle.dio.PACAKAGE_NAME.PERIPHERAL_IFACE
// following code is correct for precompiled peripheral driver that follows DAAPI name convention.
String fullName = clazz.getName();
// check for name correctness from current spec point of view.
// it is enough to check only name of class itself because of all DAAPI config class are final.
// it is assumed that neither driver nor application can create a code for com.oracle.* domain.
// it is not correct for JavaSE but running code has no super user rights.
if (-1 != fullName.indexOf(Constants.PREFIX)) {
int pIndex = fullName.indexOf('.', Constants.PREFIX.length());
try {
String pack = fullName.substring(Constants.PREFIX.length(), pIndex);
String device = fullName.substring(pIndex + 1);
return (PeripheralFactory)Class.forName(Constants.FACTORY_PREFIX + pack + Constants.IMPL + device + Constants.FACTORY).newInstance();
} catch (RuntimeException | ClassNotFoundException |
InstantiationException | IllegalAccessException e) {
}
}
throw new UnsupportedDeviceTypeException(
ExceptionMessage.format(ExceptionMessage.DEVICE_INVALID_CLASSNAME, fullName)
);
}
// is called in response to UDTE and DNFE
private static
> P loadFromDriver(PeripheralDescriptorImpl
descr, int mode) throws
DeviceNotFoundException, UnavailableDeviceException, InvalidDeviceConfigException,
UnsupportedAccessModeException, IOException {
ServiceLoader loader = ServiceLoader.load(DeviceProvider.class);
Iterator iter = loader.iterator();
final DeviceConfig config = descr.getConfiguration();
final Class
type = descr.getInterface();
boolean found = false;
try {
if (!iter.hasNext()) {
return null;
}
while (iter.hasNext()) {
DeviceProvider provider = iter.next();
try {
if (provider.getConfigType().isAssignableFrom(config.getClass()) &&
(null == type || provider.getType().equals(type))) {
found = true;
if (provider.matches(descr.getProperties())) {
// properties was checked by Registry when descriptor was loaded up
P dev = (P)provider.open(config,descr.getProperties(),mode);
// hack for config registration: save info about factory that may restore config from serialized data
descr.setDeviceProvider(provider);
return dev;
}
}
} catch (UnavailableDeviceException | InvalidDeviceConfigException | UnsupportedAccessModeException | SecurityException e) {
// driver was found but it rejects provided data or device is busy or application has no rights to use the driver.
// the driver is suitable for config class deserialization as well
descr.setDeviceProvider(provider);
throw e;
} catch (Throwable e3) {
Logging.reportError("Provider " + provider + " throws " + e3);
// try other driver.
// spec quotation:
// A compliant implementation of the DeviceManager specification MUST catch undeclared unchecked exceptions,
// unexpected values (such as null) or mismatching value types that may be thrown
// or respectively returned at any of these steps
// and MUST report these conditions to the caller as a DeviceNotFoundException.
found = true;
continue;
}
}
} catch (ServiceConfigurationError ex) {
// service framework exception equals to empty iterator
return null;
}
if (found) {
throw new DeviceNotFoundException(
ExceptionMessage.format(ExceptionMessage.DEVICE_DRIVERS_NOT_MATCH)
);
}
// no driver matches descritor, caller will throw UDTE or DNFE
return null;
}
private static void checkWithProviders(Class intf) throws UnsupportedDeviceTypeException {
Iterator iter = ServiceLoader.load(DeviceProvider.class).iterator();
try {
while (iter.hasNext()) {
DeviceProvider provider = iter.next();
try {
if (provider.getType().equals(intf)) {
return;
}
} catch (Throwable e) {
// any exception threats as type is not suuported by provider
}
}
} catch (ServiceConfigurationError ex) {
// intentionally ignored
}
throw new UnsupportedDeviceTypeException (
ExceptionMessage.format(ExceptionMessage.DEVICE_DRIVER_MISSING)
);
}
}