1 /******************************************************************************* 2 * Copyright (c) 2000, 2017 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.equinox.device; 15 16 import java.security.AccessController; 17 import java.security.PrivilegedAction; 18 import java.util.Hashtable; 19 import java.util.Vector; 20 import org.eclipse.osgi.util.NLS; 21 import org.osgi.framework.*; 22 import org.osgi.service.device.Device; 23 import org.osgi.service.device.Driver; 24 import org.osgi.service.log.LogService; 25 import org.osgi.util.tracker.ServiceTracker; 26 27 /** 28 * DriverTracker class. This class tracks all Driver services. 29 * 30 */ 31 public class DriverTracker extends ServiceTracker { 32 /** Driver service name */ 33 protected final static String clazz = "org.osgi.service.device.Driver"; //$NON-NLS-1$ 34 35 /** LogService object */ 36 protected LogTracker log; 37 38 /** Dictionary mapping DRIVER_ID strings <==> Driver ServiceReferences */ 39 protected Hashtable drivers; 40 41 /** DeviceManager object. */ 42 protected Activator manager; 43 44 /** Dictionary mapping Driver ID String => 45 * Hashtable (Device ServiceReference => cached Match objects) */ 46 protected Hashtable matches; 47 48 /** Dictionary mapping Driver ID String => 49 * Hashtable (Device ServiceReference => cached referral String) */ 50 protected Hashtable referrals; 51 52 /** 53 * Create the DriverTracker. 54 * 55 * @param manager DeviceManager object. 56 * @param device DeviceTracker we are working for. 57 */ DriverTracker(Activator manager)58 public DriverTracker(Activator manager) { 59 super(manager.context, clazz, null); 60 61 this.manager = manager; 62 log = manager.log; 63 64 drivers = new Hashtable(37); 65 matches = new Hashtable(37); 66 referrals = new Hashtable(37); 67 68 if (Activator.DEBUG) { 69 log.log(LogService.LOG_DEBUG, this + " constructor"); //$NON-NLS-1$ 70 } 71 72 open(); 73 } 74 75 /** 76 * A service is being added to the ServiceTracker. 77 * 78 * <p>This method is called before a service which matched 79 * the search parameters of the ServiceTracker is 80 * added to the ServiceTracker. This method should return the 81 * service object to be tracked for this ServiceReference. 82 * The returned service object is stored in the ServiceTracker 83 * and is available from the getService and getServices 84 * methods. 85 * 86 * @param reference Reference to service being added to the ServiceTracker. 87 * @return The service object to be tracked for the 88 * ServiceReference or <tt>null</tt> if the ServiceReference should not 89 * be tracked. 90 */ addingService(ServiceReference reference)91 public Object addingService(ServiceReference reference) { 92 if (Activator.DEBUG) { 93 log.log(reference, LogService.LOG_DEBUG, this + " adding service"); //$NON-NLS-1$ 94 } 95 96 String driver_id = getDriverID(reference); 97 98 if (drivers.get(driver_id) != null) { 99 log.log(reference, LogService.LOG_WARNING, NLS.bind(DeviceMsg.Multiple_Driver_services_with_the_same_DRIVER_ID, driver_id)); 100 101 return (null); /* don't track this driver */ 102 } 103 104 drivers.put(driver_id, reference); 105 drivers.put(reference, driver_id); 106 107 manager.driverServiceRegistered = true; 108 109 /* OSGi SPR2 Device Access 1.1 110 * Section 8.4.3 - When a new Driver service is registered, 111 * the Device Attachment Algorithm must be applied to all 112 * idle Device services. 113 * 114 * We do not refine idle Devices when the manager has not fully 115 * started or the Driver service is from a bundle just installed 116 * by the devicemanager. 117 */ 118 Bundle bundle = reference.getBundle(); 119 120 if (manager.running && !manager.locators.isUninstallCandidate(bundle)) { 121 manager.refineIdleDevices(); 122 } 123 124 return (context.getService(reference)); 125 } 126 127 /** 128 * A service tracked by the ServiceTracker has been modified. 129 * 130 * <p>This method is called when a service being tracked 131 * by the ServiceTracker has had it properties modified. 132 * 133 * @param reference Reference to service that has been modified. 134 * @param service The service object for the modified service. 135 */ modifiedService(ServiceReference reference, Object service)136 public void modifiedService(ServiceReference reference, Object service) { 137 if (Activator.DEBUG) { 138 log.log(reference, LogService.LOG_DEBUG, this + " modified service"); //$NON-NLS-1$ 139 } 140 141 String driver_id = getDriverID(reference); 142 143 String old_id = (String) drivers.get(reference); 144 145 if (!driver_id.equals(old_id)) { 146 drivers.put(driver_id, reference); 147 drivers.put(reference, driver_id); 148 drivers.remove(old_id); 149 } 150 } 151 152 /** 153 * A service tracked by the ServiceTracker is being removed. 154 * 155 * <p>This method is called after a service is no longer being tracked 156 * by the ServiceTracker. 157 * 158 * @param reference Reference to service that has been removed. 159 * @param service The service object for the removed service. 160 */ removedService(ServiceReference reference, Object service)161 public void removedService(ServiceReference reference, Object service) { 162 if (Activator.DEBUG) { 163 log.log(reference, LogService.LOG_DEBUG, this + " removing service"); //$NON-NLS-1$ 164 } 165 166 String driver_id = getDriverID(reference); 167 drivers.remove(driver_id); 168 drivers.remove(reference); 169 170 matches.remove(driver_id); 171 referrals.remove(driver_id); 172 173 context.ungetService(reference); 174 175 /* OSGi SPR2 Device Access 1.1 176 * Section 8.4.4 - When a Driver service is unregistered, 177 * the Device Attachment Algorithm must be applied to all 178 * idle Device services. 179 * 180 * We do not refine idle Devices when the manager has not fully 181 * started or the Driver service is from a bundle just installed 182 * by the devicemanager. 183 */ 184 185 Bundle bundle = reference.getBundle(); 186 187 if (manager.running && !manager.locators.isUninstallCandidate(bundle)) { 188 DriverUpdate update = new DriverUpdate(bundle, manager); 189 190 Thread thread = (new SecureAction()).createThread(update, DeviceMsg.DeviceManager_Update_Wait); 191 192 thread.start(); 193 } 194 } 195 196 /** 197 * Return the DRIVER_ID string for a ServiceReference. 198 * 199 * Per Section 8.4.3 of the OSGi SP R2 spec, 200 * "A Driver service registration must have a DRIVER_ID property" 201 * 202 * This method is somewhat more lenient. If no DRIVER_ID property 203 * is set, it will use the Bundle's location instead. 204 * 205 * @param reference Reference to driver service. 206 * @return DRIVER_ID string. 207 */ getDriverID(final ServiceReference reference)208 public String getDriverID(final ServiceReference reference) { 209 String driver_id = (String) reference.getProperty(org.osgi.service.device.Constants.DRIVER_ID); 210 211 if (driver_id == null) { 212 log.log(reference, LogService.LOG_WARNING, DeviceMsg.Driver_service_has_no_DRIVER_ID); 213 driver_id = (String) AccessController.doPrivileged(new PrivilegedAction() { 214 public Object run() { 215 return reference.getBundle().getLocation(); 216 } 217 }); 218 } 219 220 return (driver_id); 221 } 222 223 /** 224 * Get the ServiceReference for a given DRIVER_ID. 225 * 226 * @param driver_id 227 * @return ServiceReference to a Driver service. 228 */ getDriver(String driver_id)229 public ServiceReference getDriver(String driver_id) { 230 return ((ServiceReference) drivers.get(driver_id)); 231 } 232 233 /** 234 * Search the driver list to find the best match for the device. 235 * 236 * @return ServiceReference to best matched Driver or null of their is no match. 237 */ match(ServiceReference device, Vector exclude)238 public ServiceReference match(ServiceReference device, Vector exclude) { 239 if (Activator.DEBUG) { 240 log.log(device, LogService.LOG_DEBUG, this + ": Driver match called"); //$NON-NLS-1$ 241 } 242 243 ServiceReference[] references = getServiceReferences(); 244 245 if (references != null) { 246 int size = references.length; 247 248 Vector successfulMatches = new Vector(size); 249 250 for (int i = 0; i < size; i++) { 251 ServiceReference driver = references[i]; 252 253 if (exclude.contains(driver)) { 254 if (Activator.DEBUG) { 255 log.log(driver, LogService.LOG_DEBUG, this + ": Driver match excluded: " + drivers.get(driver)); //$NON-NLS-1$ 256 } 257 } else { 258 if (Activator.DEBUG) { 259 log.log(driver, LogService.LOG_DEBUG, this + ": Driver match called: " + drivers.get(driver)); //$NON-NLS-1$ 260 } 261 262 Match match = getMatch(driver, device); 263 264 if (match == null) { 265 Driver service = (Driver) getService(driver); 266 267 if (service == null) { 268 continue; 269 } 270 271 int matchValue = Device.MATCH_NONE; 272 273 try { 274 matchValue = service.match(device); 275 } catch (Throwable t) { 276 log.log(driver, LogService.LOG_ERROR, DeviceMsg.Driver_error_during_match, t); 277 278 continue; 279 } 280 281 if (Activator.DEBUG) { 282 log.log(driver, LogService.LOG_DEBUG, this + ": Driver match value: " + matchValue); //$NON-NLS-1$ 283 } 284 285 match = new Match(driver, matchValue); 286 287 storeMatch(driver, device, match); 288 } 289 290 if (match.getMatchValue() > Device.MATCH_NONE) { 291 successfulMatches.addElement(match); 292 } 293 } 294 } 295 296 size = successfulMatches.size(); 297 298 if (size > 0) { 299 Match[] matchArray = new Match[size]; 300 successfulMatches.copyInto(matchArray); 301 302 return manager.selectors.select(device, matchArray); 303 } 304 } 305 306 return null; 307 } 308 getMatch(ServiceReference driver, ServiceReference device)309 public Match getMatch(ServiceReference driver, ServiceReference device) { 310 String driverid = getDriverID(driver); 311 312 Hashtable driverMatches = (Hashtable) matches.get(driverid); 313 314 if (driverMatches == null) { 315 return null; 316 } 317 318 return (Match) driverMatches.get(device); 319 } 320 storeMatch(ServiceReference driver, ServiceReference device, Match match)321 public void storeMatch(ServiceReference driver, ServiceReference device, Match match) { 322 String driverid = getDriverID(driver); 323 324 Hashtable driverMatches = (Hashtable) matches.get(driverid); 325 326 if (driverMatches == null) { 327 driverMatches = new Hashtable(37); 328 329 matches.put(driverid, driverMatches); 330 } 331 332 driverMatches.put(device, match); 333 } 334 335 /** 336 * Attempt to attach the driver to the device. If the driver 337 * refers, add the referred driver to the driver list. 338 * 339 * @param driver Driver to attach 340 * @param device Device to be attached 341 * @return true is the Driver successfully attached. 342 */ attach(ServiceReference driver, ServiceReference device, Vector exclude)343 public boolean attach(ServiceReference driver, ServiceReference device, Vector exclude) { 344 if (Activator.DEBUG) { 345 log.log(driver, LogService.LOG_DEBUG, this + ": Driver attach called: " + drivers.get(driver)); //$NON-NLS-1$ 346 } 347 348 Driver service = (Driver) getService(driver); 349 350 if (service != null) { 351 String referral = getReferral(driver, device); 352 353 if (referral == null) { 354 try { 355 referral = service.attach(device); 356 } catch (Throwable t) { 357 log.log(driver, LogService.LOG_ERROR, DeviceMsg.Driver_error_during_attach, t); 358 359 exclude.addElement(driver); 360 361 return (false); 362 } 363 364 storeReferral(driver, device, (referral == null) ? "" : referral); //$NON-NLS-1$ 365 } else { 366 if (referral.length() == 0) { 367 referral = null; 368 } 369 } 370 371 if (referral == null) { 372 log.log(device, LogService.LOG_INFO, NLS.bind(DeviceMsg.Device_attached_by_DRIVER_ID, drivers.get(driver))); 373 374 manager.locators.usingDriverBundle(driver.getBundle()); 375 376 return (true); 377 } 378 379 log.log(device, LogService.LOG_INFO, NLS.bind(DeviceMsg.Device_referred_to, referral)); 380 manager.locators.loadDriver(referral, this); 381 } 382 383 exclude.addElement(driver); 384 385 return (false); 386 } 387 getReferral(ServiceReference driver, ServiceReference device)388 public String getReferral(ServiceReference driver, ServiceReference device) { 389 String driverid = getDriverID(driver); 390 391 Hashtable driverReferrals = (Hashtable) referrals.get(driverid); 392 393 if (driverReferrals == null) { 394 return null; 395 } 396 397 return (String) driverReferrals.get(device); 398 } 399 storeReferral(ServiceReference driver, ServiceReference device, String referral)400 public void storeReferral(ServiceReference driver, ServiceReference device, String referral) { 401 String driverid = getDriverID(driver); 402 403 Hashtable driverReferrals = (Hashtable) referrals.get(driverid); 404 405 if (driverReferrals == null) { 406 driverReferrals = new Hashtable(37); 407 408 referrals.put(driverid, driverReferrals); 409 } 410 411 driverReferrals.put(device, referral); 412 } 413 toString()414 public String toString() { 415 return "DriverTracker"; //$NON-NLS-1$ 416 } 417 418 public class DriverUpdate implements Runnable, ServiceListener, BundleListener { 419 private Activator manager_; 420 private Bundle bundle; 421 private BundleContext contxt; 422 423 /** if false the thread must terminate */ 424 private volatile boolean running; 425 426 private long updatewait; 427 DriverUpdate(Bundle bundle, Activator manager)428 DriverUpdate(Bundle bundle, Activator manager) { 429 this.manager_ = manager; 430 this.bundle = bundle; 431 432 contxt = manager_.context; 433 updatewait = manager_.updatewait; 434 running = true; 435 436 contxt.addBundleListener(this); 437 try { 438 contxt.addServiceListener(this, manager_.driverFilter.toString()); 439 } catch (InvalidSyntaxException e) { 440 /* this should not happen */ 441 } 442 } 443 run()444 public void run() { 445 // 1. Wait for some time 446 // 2. if bundle registers Driver; terminate 447 // 3. if bundle uninstalls; cancel wait 448 // 4. manager.refineIdleDevices() 449 450 try { 451 if (updatewait > 0) { 452 synchronized (this) { 453 wait(updatewait); 454 } 455 } 456 } catch (InterruptedException e) { 457 //do nothing 458 } 459 460 contxt.removeServiceListener(this); 461 contxt.removeBundleListener(this); 462 463 if (running) { 464 manager.refineIdleDevices(); 465 } 466 } 467 serviceChanged(ServiceEvent event)468 public void serviceChanged(ServiceEvent event) { 469 if ((event.getType() == ServiceEvent.REGISTERED) && bundle.equals(event.getServiceReference().getBundle())) { 470 contxt.removeServiceListener(this); 471 472 running = false; /* cancel */ 473 474 /* should probably interrupt waiting thread here */ 475 } 476 } 477 bundleChanged(BundleEvent event)478 public void bundleChanged(BundleEvent event) { 479 if ((event.getType() == Bundle.UNINSTALLED) && bundle.equals(event.getBundle())) { 480 contxt.removeBundleListener(this); 481 482 updatewait = 0; /* avoid wait */ 483 484 /* should probably interrupt waiting thread here */ 485 } 486 } 487 } 488 } 489