| /******************************************************************************* |
| * Copyright (c) 2000, 2017 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.device; |
| |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.Hashtable; |
| import java.util.Vector; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.*; |
| import org.osgi.service.device.Device; |
| import org.osgi.service.device.Driver; |
| import org.osgi.service.log.LogService; |
| import org.osgi.util.tracker.ServiceTracker; |
| |
| /** |
| * DriverTracker class. This class tracks all Driver services. |
| * |
| */ |
| public class DriverTracker extends ServiceTracker { |
| /** Driver service name */ |
| protected final static String clazz = "org.osgi.service.device.Driver"; //$NON-NLS-1$ |
| |
| /** LogService object */ |
| protected LogTracker log; |
| |
| /** Dictionary mapping DRIVER_ID strings <==> Driver ServiceReferences */ |
| protected Hashtable drivers; |
| |
| /** DeviceManager object. */ |
| protected Activator manager; |
| |
| /** Dictionary mapping Driver ID String => |
| * Hashtable (Device ServiceReference => cached Match objects) */ |
| protected Hashtable matches; |
| |
| /** Dictionary mapping Driver ID String => |
| * Hashtable (Device ServiceReference => cached referral String) */ |
| protected Hashtable referrals; |
| |
| /** |
| * Create the DriverTracker. |
| * |
| * @param manager DeviceManager object. |
| * @param device DeviceTracker we are working for. |
| */ |
| public DriverTracker(Activator manager) { |
| super(manager.context, clazz, null); |
| |
| this.manager = manager; |
| log = manager.log; |
| |
| drivers = new Hashtable(37); |
| matches = new Hashtable(37); |
| referrals = new Hashtable(37); |
| |
| if (Activator.DEBUG) { |
| log.log(LogService.LOG_DEBUG, this + " constructor"); //$NON-NLS-1$ |
| } |
| |
| open(); |
| } |
| |
| /** |
| * A service is being added to the ServiceTracker. |
| * |
| * <p>This method is called before a service which matched |
| * the search parameters of the ServiceTracker is |
| * added to the ServiceTracker. This method should return the |
| * service object to be tracked for this ServiceReference. |
| * The returned service object is stored in the ServiceTracker |
| * and is available from the getService and getServices |
| * methods. |
| * |
| * @param reference Reference to service being added to the ServiceTracker. |
| * @return The service object to be tracked for the |
| * ServiceReference or <tt>null</tt> if the ServiceReference should not |
| * be tracked. |
| */ |
| public Object addingService(ServiceReference reference) { |
| if (Activator.DEBUG) { |
| log.log(reference, LogService.LOG_DEBUG, this + " adding service"); //$NON-NLS-1$ |
| } |
| |
| String driver_id = getDriverID(reference); |
| |
| if (drivers.get(driver_id) != null) { |
| log.log(reference, LogService.LOG_WARNING, NLS.bind(DeviceMsg.Multiple_Driver_services_with_the_same_DRIVER_ID, driver_id)); |
| |
| return (null); /* don't track this driver */ |
| } |
| |
| drivers.put(driver_id, reference); |
| drivers.put(reference, driver_id); |
| |
| manager.driverServiceRegistered = true; |
| |
| /* OSGi SPR2 Device Access 1.1 |
| * Section 8.4.3 - When a new Driver service is registered, |
| * the Device Attachment Algorithm must be applied to all |
| * idle Device services. |
| * |
| * We do not refine idle Devices when the manager has not fully |
| * started or the Driver service is from a bundle just installed |
| * by the devicemanager. |
| */ |
| Bundle bundle = reference.getBundle(); |
| |
| if (manager.running && !manager.locators.isUninstallCandidate(bundle)) { |
| manager.refineIdleDevices(); |
| } |
| |
| return (context.getService(reference)); |
| } |
| |
| /** |
| * A service tracked by the ServiceTracker has been modified. |
| * |
| * <p>This method is called when a service being tracked |
| * by the ServiceTracker has had it properties modified. |
| * |
| * @param reference Reference to service that has been modified. |
| * @param service The service object for the modified service. |
| */ |
| public void modifiedService(ServiceReference reference, Object service) { |
| if (Activator.DEBUG) { |
| log.log(reference, LogService.LOG_DEBUG, this + " modified service"); //$NON-NLS-1$ |
| } |
| |
| String driver_id = getDriverID(reference); |
| |
| String old_id = (String) drivers.get(reference); |
| |
| if (!driver_id.equals(old_id)) { |
| drivers.put(driver_id, reference); |
| drivers.put(reference, driver_id); |
| drivers.remove(old_id); |
| } |
| } |
| |
| /** |
| * A service tracked by the ServiceTracker is being removed. |
| * |
| * <p>This method is called after a service is no longer being tracked |
| * by the ServiceTracker. |
| * |
| * @param reference Reference to service that has been removed. |
| * @param service The service object for the removed service. |
| */ |
| public void removedService(ServiceReference reference, Object service) { |
| if (Activator.DEBUG) { |
| log.log(reference, LogService.LOG_DEBUG, this + " removing service"); //$NON-NLS-1$ |
| } |
| |
| String driver_id = getDriverID(reference); |
| drivers.remove(driver_id); |
| drivers.remove(reference); |
| |
| matches.remove(driver_id); |
| referrals.remove(driver_id); |
| |
| context.ungetService(reference); |
| |
| /* OSGi SPR2 Device Access 1.1 |
| * Section 8.4.4 - When a Driver service is unregistered, |
| * the Device Attachment Algorithm must be applied to all |
| * idle Device services. |
| * |
| * We do not refine idle Devices when the manager has not fully |
| * started or the Driver service is from a bundle just installed |
| * by the devicemanager. |
| */ |
| |
| Bundle bundle = reference.getBundle(); |
| |
| if (manager.running && !manager.locators.isUninstallCandidate(bundle)) { |
| DriverUpdate update = new DriverUpdate(bundle, manager); |
| |
| Thread thread = (new SecureAction()).createThread(update, DeviceMsg.DeviceManager_Update_Wait); |
| |
| thread.start(); |
| } |
| } |
| |
| /** |
| * Return the DRIVER_ID string for a ServiceReference. |
| * |
| * Per Section 8.4.3 of the OSGi SP R2 spec, |
| * "A Driver service registration must have a DRIVER_ID property" |
| * |
| * This method is somewhat more lenient. If no DRIVER_ID property |
| * is set, it will use the Bundle's location instead. |
| * |
| * @param reference Reference to driver service. |
| * @return DRIVER_ID string. |
| */ |
| public String getDriverID(final ServiceReference reference) { |
| String driver_id = (String) reference.getProperty(org.osgi.service.device.Constants.DRIVER_ID); |
| |
| if (driver_id == null) { |
| log.log(reference, LogService.LOG_WARNING, DeviceMsg.Driver_service_has_no_DRIVER_ID); |
| driver_id = (String) AccessController.doPrivileged(new PrivilegedAction() { |
| public Object run() { |
| return reference.getBundle().getLocation(); |
| } |
| }); |
| } |
| |
| return (driver_id); |
| } |
| |
| /** |
| * Get the ServiceReference for a given DRIVER_ID. |
| * |
| * @param driver_id |
| * @return ServiceReference to a Driver service. |
| */ |
| public ServiceReference getDriver(String driver_id) { |
| return ((ServiceReference) drivers.get(driver_id)); |
| } |
| |
| /** |
| * Search the driver list to find the best match for the device. |
| * |
| * @return ServiceReference to best matched Driver or null of their is no match. |
| */ |
| public ServiceReference match(ServiceReference device, Vector exclude) { |
| if (Activator.DEBUG) { |
| log.log(device, LogService.LOG_DEBUG, this + ": Driver match called"); //$NON-NLS-1$ |
| } |
| |
| ServiceReference[] references = getServiceReferences(); |
| |
| if (references != null) { |
| int size = references.length; |
| |
| Vector successfulMatches = new Vector(size); |
| |
| for (int i = 0; i < size; i++) { |
| ServiceReference driver = references[i]; |
| |
| if (exclude.contains(driver)) { |
| if (Activator.DEBUG) { |
| log.log(driver, LogService.LOG_DEBUG, this + ": Driver match excluded: " + drivers.get(driver)); //$NON-NLS-1$ |
| } |
| } else { |
| if (Activator.DEBUG) { |
| log.log(driver, LogService.LOG_DEBUG, this + ": Driver match called: " + drivers.get(driver)); //$NON-NLS-1$ |
| } |
| |
| Match match = getMatch(driver, device); |
| |
| if (match == null) { |
| Driver service = (Driver) getService(driver); |
| |
| if (service == null) { |
| continue; |
| } |
| |
| int matchValue = Device.MATCH_NONE; |
| |
| try { |
| matchValue = service.match(device); |
| } catch (Throwable t) { |
| log.log(driver, LogService.LOG_ERROR, DeviceMsg.Driver_error_during_match, t); |
| |
| continue; |
| } |
| |
| if (Activator.DEBUG) { |
| log.log(driver, LogService.LOG_DEBUG, this + ": Driver match value: " + matchValue); //$NON-NLS-1$ |
| } |
| |
| match = new Match(driver, matchValue); |
| |
| storeMatch(driver, device, match); |
| } |
| |
| if (match.getMatchValue() > Device.MATCH_NONE) { |
| successfulMatches.addElement(match); |
| } |
| } |
| } |
| |
| size = successfulMatches.size(); |
| |
| if (size > 0) { |
| Match[] matchArray = new Match[size]; |
| successfulMatches.copyInto(matchArray); |
| |
| return manager.selectors.select(device, matchArray); |
| } |
| } |
| |
| return null; |
| } |
| |
| public Match getMatch(ServiceReference driver, ServiceReference device) { |
| String driverid = getDriverID(driver); |
| |
| Hashtable driverMatches = (Hashtable) matches.get(driverid); |
| |
| if (driverMatches == null) { |
| return null; |
| } |
| |
| return (Match) driverMatches.get(device); |
| } |
| |
| public void storeMatch(ServiceReference driver, ServiceReference device, Match match) { |
| String driverid = getDriverID(driver); |
| |
| Hashtable driverMatches = (Hashtable) matches.get(driverid); |
| |
| if (driverMatches == null) { |
| driverMatches = new Hashtable(37); |
| |
| matches.put(driverid, driverMatches); |
| } |
| |
| driverMatches.put(device, match); |
| } |
| |
| /** |
| * Attempt to attach the driver to the device. If the driver |
| * refers, add the referred driver to the driver list. |
| * |
| * @param driver Driver to attach |
| * @param device Device to be attached |
| * @return true is the Driver successfully attached. |
| */ |
| public boolean attach(ServiceReference driver, ServiceReference device, Vector exclude) { |
| if (Activator.DEBUG) { |
| log.log(driver, LogService.LOG_DEBUG, this + ": Driver attach called: " + drivers.get(driver)); //$NON-NLS-1$ |
| } |
| |
| Driver service = (Driver) getService(driver); |
| |
| if (service != null) { |
| String referral = getReferral(driver, device); |
| |
| if (referral == null) { |
| try { |
| referral = service.attach(device); |
| } catch (Throwable t) { |
| log.log(driver, LogService.LOG_ERROR, DeviceMsg.Driver_error_during_attach, t); |
| |
| exclude.addElement(driver); |
| |
| return (false); |
| } |
| |
| storeReferral(driver, device, (referral == null) ? "" : referral); //$NON-NLS-1$ |
| } else { |
| if (referral.length() == 0) { |
| referral = null; |
| } |
| } |
| |
| if (referral == null) { |
| log.log(device, LogService.LOG_INFO, NLS.bind(DeviceMsg.Device_attached_by_DRIVER_ID, drivers.get(driver))); |
| |
| manager.locators.usingDriverBundle(driver.getBundle()); |
| |
| return (true); |
| } |
| |
| log.log(device, LogService.LOG_INFO, NLS.bind(DeviceMsg.Device_referred_to, referral)); |
| manager.locators.loadDriver(referral, this); |
| } |
| |
| exclude.addElement(driver); |
| |
| return (false); |
| } |
| |
| public String getReferral(ServiceReference driver, ServiceReference device) { |
| String driverid = getDriverID(driver); |
| |
| Hashtable driverReferrals = (Hashtable) referrals.get(driverid); |
| |
| if (driverReferrals == null) { |
| return null; |
| } |
| |
| return (String) driverReferrals.get(device); |
| } |
| |
| public void storeReferral(ServiceReference driver, ServiceReference device, String referral) { |
| String driverid = getDriverID(driver); |
| |
| Hashtable driverReferrals = (Hashtable) referrals.get(driverid); |
| |
| if (driverReferrals == null) { |
| driverReferrals = new Hashtable(37); |
| |
| referrals.put(driverid, driverReferrals); |
| } |
| |
| driverReferrals.put(device, referral); |
| } |
| |
| public String toString() { |
| return "DriverTracker"; //$NON-NLS-1$ |
| } |
| |
| public class DriverUpdate implements Runnable, ServiceListener, BundleListener { |
| private Activator manager_; |
| private Bundle bundle; |
| private BundleContext contxt; |
| |
| /** if false the thread must terminate */ |
| private volatile boolean running; |
| |
| private long updatewait; |
| |
| DriverUpdate(Bundle bundle, Activator manager) { |
| this.manager_ = manager; |
| this.bundle = bundle; |
| |
| contxt = manager_.context; |
| updatewait = manager_.updatewait; |
| running = true; |
| |
| contxt.addBundleListener(this); |
| try { |
| contxt.addServiceListener(this, manager_.driverFilter.toString()); |
| } catch (InvalidSyntaxException e) { |
| /* this should not happen */ |
| } |
| } |
| |
| public void run() { |
| // 1. Wait for some time |
| // 2. if bundle registers Driver; terminate |
| // 3. if bundle uninstalls; cancel wait |
| // 4. manager.refineIdleDevices() |
| |
| try { |
| if (updatewait > 0) { |
| synchronized (this) { |
| wait(updatewait); |
| } |
| } |
| } catch (InterruptedException e) { |
| //do nothing |
| } |
| |
| contxt.removeServiceListener(this); |
| contxt.removeBundleListener(this); |
| |
| if (running) { |
| manager.refineIdleDevices(); |
| } |
| } |
| |
| public void serviceChanged(ServiceEvent event) { |
| if ((event.getType() == ServiceEvent.REGISTERED) && bundle.equals(event.getServiceReference().getBundle())) { |
| contxt.removeServiceListener(this); |
| |
| running = false; /* cancel */ |
| |
| /* should probably interrupt waiting thread here */ |
| } |
| } |
| |
| public void bundleChanged(BundleEvent event) { |
| if ((event.getType() == Bundle.UNINSTALLED) && bundle.equals(event.getBundle())) { |
| contxt.removeBundleListener(this); |
| |
| updatewait = 0; /* avoid wait */ |
| |
| /* should probably interrupt waiting thread here */ |
| } |
| } |
| } |
| } |