Package elisa :: Package plugins :: Package good :: Package hal :: Module hal_service
[hide private]
[frames] | no frames]

Source Code for Module elisa.plugins.good.hal.hal_service

  1  # -*- coding: utf-8 -*- 
  2  # Elisa - Home multimedia server 
  3  # Copyright (C) 2006-2008 Fluendo Embedded S.L. (www.fluendo.com). 
  4  # All rights reserved. 
  5  # 
  6  # This file is available under one of two license agreements. 
  7  # 
  8  # This file is licensed under the GPL version 3. 
  9  # See "LICENSE.GPL" in the root of this distribution including a special 
 10  # exception to use Elisa with Fluendo's plugins. 
 11  # 
 12  # The GPL part of Elisa is also available under a commercial licensing 
 13  # agreement from Fluendo. 
 14  # See "LICENSE.Elisa" in the root directory of this distribution package 
 15  # for details on that license. 
 16   
 17  """ 
 18  HalServiceProvider component class 
 19  """ 
 20   
 21   
 22  __maintainer__ = 'Philippe Normand <philippe@fluendo.com>' 
 23  __maintainer2__ = 'Benjamin Kampmann <benjamin@fluendo.com>' 
 24   
 25   
 26  from elisa.base_components.service_provider import ServiceProvider 
 27  from elisa.core.bus.bus_message import ComponentsLoaded, DeviceAction 
 28  from elisa.core import common 
 29  from elisa.core import component 
 30  import os, time, platform 
 31  from twisted.internet import threads 
 32   
 33  from elisa.extern.translation import gettexter, N_ 
 34  T_ = gettexter('elisa-hal-service') 
 35   
 36  import dbus 
 37  if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): 
 38      import dbus.glib 
 39  from dbus.exceptions import DBusException 
 40   
 41  try: 
 42      import gconf 
 43  except ImportError: 
 44      gconf = None 
 45   
46 -class NotMountable(Exception):
47 pass
48
49 -def get_dbus_error_message(exception):
50 """ 51 Retrieve the message of the DBus error. Usually something like 52 'org.freedesktop....' 53 54 @param exception: the DBus exception to analyze 55 @type exception: L{dbus.DBusException} 56 @rtype: string 57 """ 58 if dbus.version < (0, 82, 0): 59 msg = exception.message 60 else: 61 msg = "%s: %s" % (exception._dbus_error_name, exception.message) 62 return msg
63 64
65 -class HalService(ServiceProvider):
66 """ 67 This class implements HAL support 68 """ 69
70 - def __init__(self):
71 ServiceProvider.__init__(self) 72 self._hotplugged_devices = {}
73
74 - def initialize(self):
75 ServiceProvider.initialize(self) 76 bus = common.application.bus 77 bus.register(self._components_loaded, ComponentsLoaded) 78 bus.register(self._device_action, DeviceAction) 79 80 try: 81 self.bus = dbus.SystemBus() 82 self.ses = dbus.SessionBus() 83 except DBusException, error: 84 msg = "DBus is not running." 85 raise component.InitializeFailure(self.name, msg) 86 87 try: 88 self.hal_manager = self.bus.get_object('org.freedesktop.Hal', 89 '/org/freedesktop/Hal/Manager') 90 except DBusException, error: 91 self.warning(error) 92 msg = "HAL is not installed or might not be running." 93 raise component.InitializeFailure(self.name, msg) 94 95 self.hal_iface = dbus.Interface(self.hal_manager, 96 'org.freedesktop.Hal.Manager')
97
98 - def _device_action(self, msg, sender):
99 if msg.action == DeviceAction.ActionType.EJECT: 100 for udi, infos in self._hotplugged_devices.iteritems(): 101 (name, fstype, mount_point) = infos 102 if fstype == msg.fstype and mount_point == msg.mount_point: 103 device = self.get_device_with_udi(udi) 104 self.eject_device(device) 105 break
106
107 - def _components_loaded(self, msg, sender):
108 self.detect_coldplugged() 109 self.hal_iface.connect_to_signal('DeviceAdded', 110 self.device_added_callback) 111 self.hal_iface.connect_to_signal('DeviceRemoved', 112 self.device_removed_callback)
113
114 - def start(self):
116
117 - def stop(self):
119
120 - def detect_coldplugged(self):
121 udis = self.hal_iface.FindDeviceByCapability('volume') 122 123 for udi in udis: 124 # get a hal object for the volume referenced by udi 125 volume = self.get_device_with_udi(udi) 126 parent_uri = volume.GetProperty('info.parent') 127 parent = self.get_device_with_udi(parent_uri) 128 129 if (parent.GetProperty('storage.removable') \ 130 or parent.GetProperty('storage.removable.media_available')) \ 131 and (parent.GetProperty('storage.hotpluggable') or 132 volume.GetProperty('volume.is_disc')): 133 # if its parent is removable and hotpluggable or the volume 134 # is a disc (DVD or CD) then the volume has been coldplugged 135 self.device_added_callback(udi)
136
138 """ 139 Neutralize some of the volume_manager monitoring settings so 140 that the user won't see rhythmbox pop up when an iPod is 141 inserted (for example). 142 """ 143 if gconf: 144 client = gconf.client_get_default() 145 path = '/desktop/gnome/volume_manager' 146 autoplay_cda = client.get_bool('%s/autoplay_cda' % path) 147 autoplay_dvd = client.get_bool('%s/autoplay_dvd' % path) 148 autobrowse = client.get_bool('%s/autobrowse' % path) 149 self.volume_manager_config = {'autoplay_cda': autoplay_cda, 150 'autoplay_dvd': autoplay_dvd, 151 'autobrowse': autobrowse} 152 for prop in self.volume_manager_config.keys(): 153 client.set_bool('%s/%s' % (path, prop), False)
154
156 """ 157 Restore the volume_manager gconf settings 158 """ 159 if gconf: 160 client = gconf.client_get_default() 161 path = '/desktop/gnome/volume_manager' 162 for prop, value in self.volume_manager_config.iteritems(): 163 client.set_bool('%s/%s' % (path, prop), value)
164
165 - def get_device_with_udi(self, udi):
166 obj = self.bus.get_object('org.freedesktop.Hal', udi) 167 if obj != None: 168 device = dbus.Interface(obj, 'org.freedesktop.Hal.Device') 169 return device 170 return None
171
172 - def parent_is_ipod(self, device):
173 parent_udi = device.GetProperty(u'info.parent') 174 parent = self.get_device_with_udi(parent_udi) 175 if not parent.PropertyExists(u'portable_audio_player.type'): 176 return False 177 return parent.GetProperty(u'portable_audio_player.type').lower() == 'ipod'
178 # Is it intelligent to try to get the portable_audio_player.output_formats 179 # too for accessing in audio or video? 180
181 - def eject_device(self, device):
182 name = device.GetProperty(u'info.product') 183 self.debug("ejecting: %r", name) 184 interface = 'org.freedesktop.Hal.Device.Storage' 185 method = device.get_dbus_method('Eject', dbus_interface=interface) 186 187 try: 188 method([]) 189 except ValueError: 190 # not exposed on HAL DBus API 191 192 block_device = device.GetProperty(u'block.device') 193 # TODO: find something more cross-platform 194 threads.deferToThread(os.system, "eject %s" % block_device)
195 196
197 - def mount_device(self, device):
198 ignore = self._get_property(device, u'volume.ignore', False) 199 200 if ignore: 201 self.debug("volume.ignore property set on %r, can't mount it", 202 device) 203 return 204 205 prop_name = u'org.freedesktop.Hal.Device.Volume.method_names' 206 method_names = self._get_property(device, prop_name, []) 207 208 if 'Mount' not in method_names: 209 raise NotMountable() 210 211 interface = 'org.freedesktop.Hal.Device.Volume' 212 method = device.get_dbus_method('Mount', dbus_interface=interface) 213 214 name = device.GetProperty(u'info.product') 215 self.debug("mounting: %r ", name) 216 217 try: 218 # Let's HAL decide, what the mount point should be 219 method('', device.GetProperty(u'volume.fstype'),[]) 220 except DBusException, exc: 221 already_mounted = 'org.freedesktop.Hal.Device.Volume.AlreadyMounted' 222 unavailable = 'org.freedesktop.Hal.Device.Volume.MountPointNotAvailable' 223 permission_denied = 'org.freedesktop.Hal.Device.Volume.PermissionDenied' 224 225 msg = get_dbus_error_message(exc) 226 227 if msg.startswith(already_mounted): 228 self.info("Already mounted") 229 elif msg.startswith(permission_denied): 230 idx = msg.index(permission_denied) + len(permission_denied) + 2 231 device_error = msg[idx:] 232 self.info("Permission denied: %s", device_error) 233 elif msg.startswith(unavailable): 234 return None 235 else: 236 raise 237 238 mount_point = self._get_mount_point(device) 239 return mount_point
240
241 - def _get_mount_point(self, device):
242 mount_point = device.GetProperty(u'volume.mount_point') 243 name = device.GetProperty(u'info.product') 244 if not mount_point: 245 if platform.system() == 'Linux': 246 # this property is not supported by upstream HAL 247 # and seems specific to Ubuntu distro as stated there: 248 # http://lists.freedesktop.org/archives/hal/2007-April/008062.html 249 # FIXME: this needs further investigation. linux.* properties 250 # should not be used. 251 mount_point = self._get_property(device, 252 'linux.fstab.mountpoint','') 253 254 self.debug("mount point of %r: %r", name, mount_point) 255 return mount_point
256
257 - def _get_property(self, device, prop_name, default):
258 value = default 259 if device.PropertyExists(prop_name): 260 value = device.GetProperty(prop_name) 261 return value
262 263
264 - def device_added_callback(self, udi):
265 mount_point_prop = u'volume.label' 266 267 if udi not in self._hotplugged_devices: 268 device = self.get_device_with_udi(udi) 269 if device.QueryCapability("volume"): 270 try: 271 name = str(device.GetProperty('info.product')) 272 except dbus.DBusException: 273 name = '' 274 275 if self.parent_is_ipod(device): 276 fstype = 'ipod' 277 try: 278 point = self.mount_device(device) 279 # we support audio-only iPod for now 280 self.new_volume_cb(udi, name, fstype, point, 281 ['audio',], True, 'ipod') 282 except NotMountable, exception: 283 pass 284 else: 285 286 parent_udi = device.GetProperty(u'info.parent') 287 parent = self.get_device_with_udi(parent_udi) 288 289 removable = self._get_property(parent, u'storage.removable', 290 False) 291 292 dvd = self._get_property(device, 'volume.disc.is_videodvd', 293 False) 294 audio_cd = self._get_property(device, 295 'volume.disc.has_audio', 296 False) 297 disc_is_partition = self._get_property(device, 298 'volume.disc.is_partition', 299 False) 300 is_partition = self._get_property(device, 301 'volume.is_partition', 302 False) 303 is_partition = disc_is_partition or is_partition 304 305 has_data = self._get_property(device, 306 'volume.disc.has_data', False) 307 308 self.debug("name: %r, dvd: %r, audio_cd: %r, partition: %r "\ 309 "has_data: %r, removable: %r", name, dvd, 310 audio_cd, is_partition, has_data, removable) 311 if dvd: 312 if not name: 313 name = T_(N_('DVD')) 314 self.new_volume_cb(udi, name, 'dvd', '', ['video',], 315 removable, 'dvd') 316 elif audio_cd: 317 if not name: 318 name = T_(N_('Audio CD')) 319 fstype = 'cdda' 320 # FIXME: We are currently not supporting different 321 # devices. So the name is not sent! 322 self.new_volume_cb(udi, name, fstype,'', ['audio',], 323 removable, 'audiocd') 324 elif is_partition or has_data or removable: 325 fstype = 'file' 326 if not name: 327 name = T_(N_('File device')) 328 try: 329 point = self.mount_device(device) 330 except NotMountable, exc: 331 pass 332 else: 333 self.new_volume_cb(udi,name, fstype, point, 334 ['audio', 'video', 'image'], 335 removable, 'usb_storage')
336
337 - def device_removed_callback(self, uid):
338 if uid in self._hotplugged_devices: 339 name, fstype, mount_point = self._hotplugged_devices[uid] 340 del self._hotplugged_devices[uid] 341 if self.del_volume_cb: 342 if mount_point: 343 self.del_volume_cb(name, fstype, mount_point) 344 if fstype == 'cdda': 345 # FIXME: add support for AudioCD removal 346 self.del_volume_cb(T_(N_('Audio CD')),'cdda','') 347 elif fstype == 'dvd': 348 self.del_volume_cb(T_(N_('DVD')), 'dvd', '')
349
350 - def new_volume_cb(self, uid, name, fstype, mount_point, media_types=None, 351 removable=False, theme_icon=None):
352 """ 353 Called when a new volume has been detected 354 355 @param name: Name of the device 356 @type name: string 357 @param fstype: Filesystem type 358 @type fstype: string 359 @param mount_point: Mount point 360 @type mount_point: string 361 @keyword media_types: media types that can be stored on the volume 362 @type media_types: list 363 @keyword removable: can the volume be removed? 364 @type removable: bool 365 @keyword theme_icon: the theme_icon the view should use to display 366 this volume 367 @type theme_icon: string 368 """ 369 self._hotplugged_devices[uid] = ( name, fstype, mount_point ) 370 self.debug("New volume found %r at %r", name, mount_point) 371 372 mount_point = "%s://%s" % (fstype, mount_point) 373 msg = DeviceAction(DeviceAction.ActionType.LOCATION_ADDED, 374 name, fstype, mount_point, media_types, 375 removable, theme_icon) 376 common.application.bus.send_message(msg)
377
378 - def del_volume_cb(self, name, fstype, mount_point):
379 """ 380 Called when volume has been removed 381 382 @param name: Name of the device 383 @type name: string 384 @param fstype: Filesystem type 385 @type fstype: string 386 @param mount_point: Mount point 387 @type mount_point: string 388 """ 389 self.debug("Volume unmounted %s" % name) 390 mount_point = "%s://%s" % (fstype, mount_point) 391 msg = DeviceAction(DeviceAction.ActionType.LOCATION_REMOVED, 392 name, fstype, mount_point) 393 common.application.bus.send_message(msg)
394