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