only call IPlayerCallback::OnPlayBackSpeedChanged if the speed has actually changed
[vuplus_xbmc] / xbmc / storage / linux / UDisksProvider.cpp
1 /*
2  *      Copyright (C) 2005-2009 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21 #include "UDisksProvider.h"
22 #ifdef HAS_DBUS
23 #include "settings/AdvancedSettings.h"
24 #include "guilib/LocalizeStrings.h"
25 #include "utils/log.h"
26 #include "utils/URIUtils.h"
27
28 CUDiskDevice::CUDiskDevice(const char *DeviceKitUDI)
29 {
30   m_DeviceKitUDI = DeviceKitUDI;
31   m_UDI = "";
32   m_MountPath = "";
33   m_FileSystem = "";
34   m_isMounted = false;
35   m_isMountedByUs = false;
36   m_isRemovable = false;
37   m_isPartition = false;
38   m_isFileSystem = false;
39   m_isSystemInternal = false;
40   m_isOptical = false;
41   m_PartitionSizeGiB = 0.0f;
42   Update();
43 }
44
45 void CUDiskDevice::Update()
46 {
47   CVariant properties = CDBusUtil::GetAll("org.freedesktop.UDisks", m_DeviceKitUDI.c_str(), "org.freedesktop.UDisks.Device");
48
49   m_isFileSystem = CStdString(properties["IdUsage"].asString()) == "filesystem";
50   if (m_isFileSystem)
51   {
52     m_UDI         = properties["IdUuid"].asString();
53     m_Label       = properties["IdLabel"].asString();
54     m_FileSystem  = properties["IdType"].asString();
55   }
56   else
57   {
58     m_UDI.clear();
59     m_Label.clear();
60     m_FileSystem.clear();
61   }
62
63   m_isMounted   = properties["DeviceIsMounted"].asBoolean();
64   if (m_isMounted && properties["DeviceMountPaths"].size() > 0)
65     m_MountPath   = properties["DeviceMountPaths"][0].asString();
66   else
67     m_MountPath.clear();
68
69   m_PartitionSizeGiB = properties["PartitionSize"].asUnsignedInteger() / 1024.0 / 1024.0 / 1024.0;
70   m_isPartition = properties["DeviceIsPartition"].asBoolean();
71   m_isSystemInternal = properties["DeviceIsSystemInternal"].asBoolean();
72   m_isOptical = properties["DeviceIsOpticalDisc"].asBoolean();
73   if (m_isPartition)
74   {
75     CVariant isRemovable = CDBusUtil::GetVariant("org.freedesktop.UDisks", properties["PartitionSlave"].asString().c_str(), "org.freedesktop.UDisks.Device", "DeviceIsRemovable");
76
77     if ( !isRemovable.isNull() )
78       m_isRemovable = isRemovable.asBoolean();
79     else
80       m_isRemovable = false;
81   }
82   else
83     m_isRemovable = properties["DeviceIsRemovable"].asBoolean();
84 }
85
86 bool CUDiskDevice::Mount()
87 {
88   if (!m_isMounted && !m_isSystemInternal && m_isFileSystem)
89   {
90     CLog::Log(LOGDEBUG, "UDisks: Mounting %s", m_DeviceKitUDI.c_str());
91     CDBusMessage message("org.freedesktop.UDisks", m_DeviceKitUDI.c_str(), "org.freedesktop.UDisks.Device", "FilesystemMount");
92     message.AppendArgument("");
93     const char *array[] = {};
94     message.AppendArgument(array, 0);
95
96     DBusMessage *reply = message.SendSystem();
97     if (reply)
98     {
99       char *mountPoint;
100       if (dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &mountPoint, DBUS_TYPE_INVALID))
101       {
102         m_MountPath = mountPoint;
103         CLog::Log(LOGDEBUG, "UDisks: Sucessfully mounted %s on %s", m_DeviceKitUDI.c_str(), mountPoint);
104         m_isMountedByUs = m_isMounted = true;
105       }
106     }
107
108     return m_isMounted;
109   }
110   else
111     CLog::Log(LOGDEBUG, "UDisks: Is not able to mount %s", toString().c_str());
112
113   return false;
114 }
115
116 bool CUDiskDevice::UnMount()
117 {
118   if (m_isMounted && !m_isSystemInternal && m_isFileSystem)
119   {
120     CDBusMessage message("org.freedesktop.UDisks", m_DeviceKitUDI.c_str(), "org.freedesktop.UDisks.Device", "FilesystemUnmount");
121
122     const char *array[1];
123     message.AppendArgument(array, 0);
124
125     DBusMessage *reply = message.SendSystem();
126     if (reply)
127       m_isMountedByUs = m_isMounted = false;
128
129     return !m_isMounted;
130   }
131   else
132     CLog::Log(LOGDEBUG, "UDisks: Is not able to unmount %s", toString().c_str());
133
134   return false;
135 }
136
137 CMediaSource CUDiskDevice::ToMediaShare()
138 {
139   CMediaSource source;
140   source.strPath = m_MountPath;
141   if (m_Label.empty())
142     source.strName.Format("%.1f GB %s", m_PartitionSizeGiB, g_localizeStrings.Get(155).c_str());
143   else
144     source.strName = m_Label;
145   if (m_isOptical)
146     source.m_iDriveType = CMediaSource::SOURCE_TYPE_DVD;
147   else if (m_isSystemInternal)
148     source.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL;
149   else
150     source.m_iDriveType = CMediaSource::SOURCE_TYPE_REMOVABLE;
151   source.m_ignore = true;
152   return source;
153 }
154
155 bool CUDiskDevice::IsApproved()
156 {
157   return (m_isFileSystem && m_isMounted && m_UDI.length() > 0 && (m_FileSystem.length() > 0 && !m_FileSystem.Equals("swap")) && !m_MountPath.Equals("/")) || m_isOptical;
158 }
159
160 #define BOOL2SZ(b) ((b) ? "true" : "false")
161
162 CStdString CUDiskDevice::toString()
163 {
164   CStdString str;
165   str.Format("DeviceUDI %s: IsFileSystem %s HasFileSystem %s "
166       "IsSystemInternal %s IsMounted %s IsRemovable %s IsPartition %s "
167       "IsOptical %s",
168       m_DeviceKitUDI.c_str(), BOOL2SZ(m_isFileSystem), m_FileSystem,
169       BOOL2SZ(m_isSystemInternal), BOOL2SZ(m_isMounted),
170       BOOL2SZ(m_isRemovable), BOOL2SZ(m_isPartition), BOOL2SZ(m_isOptical));
171
172   return str;
173 }
174
175 CUDisksProvider::CUDisksProvider()
176 {
177   dbus_error_init (&m_error);
178   // TODO: do not use dbus_connection_pop_message() that requires the use of a
179   // private connection
180   m_connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &m_error);
181
182   if (m_connection)
183   {
184     dbus_connection_set_exit_on_disconnect(m_connection, false);
185
186     dbus_bus_add_match(m_connection, "type='signal',interface='org.freedesktop.UDisks'", &m_error);
187     dbus_connection_flush(m_connection);
188   }
189
190   if (dbus_error_is_set(&m_error))
191   {
192     CLog::Log(LOGERROR, "UDisks: Failed to attach to signal %s", m_error.message);
193     dbus_connection_close(m_connection);
194     dbus_connection_unref(m_connection);
195     m_connection = NULL;
196   }
197 }
198
199 CUDisksProvider::~CUDisksProvider()
200 {
201   DeviceMap::iterator itr;
202
203   for (itr = m_AvailableDevices.begin(); itr != m_AvailableDevices.end(); ++itr)
204     delete m_AvailableDevices[itr->first];
205
206   m_AvailableDevices.clear();
207
208   if (m_connection)
209   {
210     dbus_connection_close(m_connection);
211     dbus_connection_unref(m_connection);
212     m_connection = NULL;
213   }
214
215   dbus_error_free (&m_error);
216 }
217
218 void CUDisksProvider::Initialize()
219 {
220   CLog::Log(LOGDEBUG, "Selected UDisks as storage provider");
221   m_DaemonVersion = atoi(CDBusUtil::GetVariant("org.freedesktop.UDisks", "/org/freedesktop/UDisks", "org.freedesktop.UDisks", "DaemonVersion").asString().c_str());
222   CLog::Log(LOGDEBUG, "UDisks: DaemonVersion %i", m_DaemonVersion);
223
224   CLog::Log(LOGDEBUG, "UDisks: Querying available devices");
225   std::vector<CStdString> devices = EnumerateDisks();
226   for (unsigned int i = 0; i < devices.size(); i++)
227     DeviceAdded(devices[i].c_str(), NULL);
228 }
229
230 bool CUDisksProvider::Eject(CStdString mountpath)
231 {
232   DeviceMap::iterator itr;
233   CStdString path(mountpath);
234   URIUtils::RemoveSlashAtEnd(path);
235
236   for (itr = m_AvailableDevices.begin(); itr != m_AvailableDevices.end(); ++itr)
237   {
238     CUDiskDevice *device = itr->second;
239     if (device->m_MountPath.Equals(path))
240       return device->UnMount();
241   }
242
243   return false;
244 }
245
246 std::vector<CStdString> CUDisksProvider::GetDiskUsage()
247 {
248   std::vector<CStdString> devices;
249   DeviceMap::iterator itr;
250
251   for(itr = m_AvailableDevices.begin(); itr != m_AvailableDevices.end(); ++itr)
252   {
253     CUDiskDevice *device = itr->second;
254     if (device->m_isMounted)
255     {
256       CStdString str;
257       str.Format("%s %.1f GiB", device->m_MountPath.c_str(), device->m_PartitionSizeGiB);
258       devices.push_back(str);
259     }
260   }
261
262   return devices;
263 }
264
265 bool CUDisksProvider::PumpDriveChangeEvents(IStorageEventsCallback *callback)
266 {
267   bool result = false;
268   if (m_connection)
269   {
270     dbus_connection_read_write(m_connection, 0);
271     DBusMessage *msg = dbus_connection_pop_message(m_connection);
272
273     if (msg)
274     {
275       char *object;
276       if (dbus_message_get_args (msg, NULL, DBUS_TYPE_OBJECT_PATH, &object, DBUS_TYPE_INVALID))
277       {
278         result = true;
279         if (dbus_message_is_signal(msg, "org.freedesktop.UDisks", "DeviceAdded"))
280           DeviceAdded(object, callback);
281         else if (dbus_message_is_signal(msg, "org.freedesktop.UDisks", "DeviceRemoved"))
282           DeviceRemoved(object, callback);
283         else if (dbus_message_is_signal(msg, "org.freedesktop.UDisks", "DeviceChanged"))
284           DeviceChanged(object, callback);
285       }
286       dbus_message_unref(msg);
287     }
288   }
289   return result;
290 }
291
292 bool CUDisksProvider::HasUDisks()
293 {
294   bool hasUDisks = false;
295   CDBusMessage message("org.freedesktop.UDisks", "/org/freedesktop/UDisks", "org.freedesktop.UDisks", "EnumerateDevices");
296
297   DBusError error;
298   dbus_error_init (&error);
299   DBusConnection *con = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
300
301   if (con)
302     message.Send(con, &error);
303
304   if (!dbus_error_is_set(&error))
305     hasUDisks = true;
306   else
307     CLog::Log(LOGDEBUG, "UDisks: %s - %s", error.name, error.message);
308
309   dbus_error_free (&error);
310   if (con)
311     dbus_connection_unref(con);
312
313   return hasUDisks;
314 }
315
316 void CUDisksProvider::DeviceAdded(const char *object, IStorageEventsCallback *callback)
317 {
318   CLog::Log(LOGDEBUG, "UDisks: DeviceAdded (%s)", object);
319
320   if (m_AvailableDevices[object])
321   {
322     CLog::Log(LOGWARNING, "UDisks: Inconsistency found! DeviceAdded on an indexed disk");
323     delete m_AvailableDevices[object];
324   }
325
326   CUDiskDevice *device = NULL;
327     device = new CUDiskDevice(object);
328   m_AvailableDevices[object] = device;
329
330   if (g_advancedSettings.m_handleMounting)
331     device->Mount();
332
333   CLog::Log(LOGDEBUG, "UDisks: DeviceAdded - %s", device->toString().c_str());
334   if (device->m_isMounted && device->IsApproved())
335   {
336     CLog::Log(LOGNOTICE, "UDisks: Added %s", device->m_MountPath.c_str());
337     if (callback)
338       callback->OnStorageAdded(device->m_Label, device->m_MountPath);
339   }
340 }
341
342 void CUDisksProvider::DeviceRemoved(const char *object, IStorageEventsCallback *callback)
343 {
344   CLog::Log(LOGDEBUG, "UDisks: DeviceRemoved (%s)", object);
345
346   CUDiskDevice *device = m_AvailableDevices[object];
347   if (device)
348   {
349     if (device->m_isMounted && callback)
350       callback->OnStorageUnsafelyRemoved(device->m_Label);
351
352     delete m_AvailableDevices[object];
353     m_AvailableDevices.erase(object);
354   }
355 }
356
357 void CUDisksProvider::DeviceChanged(const char *object, IStorageEventsCallback *callback)
358 {
359   CLog::Log(LOGDEBUG, "UDisks: DeviceChanged (%s)", object);
360
361   CUDiskDevice *device = m_AvailableDevices[object];
362   if (device == NULL)
363   {
364     CLog::Log(LOGWARNING, "UDisks: Inconsistency found! DeviceChanged on an unindexed disk");
365     DeviceAdded(object, callback);
366   }
367   else
368   {
369     bool mounted = device->m_isMounted;
370     device->Update();
371     if (!mounted && device->m_isMounted && callback)
372       callback->OnStorageAdded(device->m_Label, device->m_MountPath);
373     else if (mounted && !device->m_isMounted && callback)
374       callback->OnStorageSafelyRemoved(device->m_Label);
375
376     CLog::Log(LOGDEBUG, "UDisks: DeviceChanged - %s", device->toString().c_str());
377   }
378 }
379
380 std::vector<CStdString> CUDisksProvider::EnumerateDisks()
381 {
382   std::vector<CStdString> devices;
383   CDBusMessage message("org.freedesktop.UDisks", "/org/freedesktop/UDisks", "org.freedesktop.UDisks", "EnumerateDevices");
384   DBusMessage *reply = message.SendSystem();
385   if (reply)
386   {
387     char** disks  = NULL;
388     int    length = 0;
389
390     if (dbus_message_get_args (reply, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &disks, &length, DBUS_TYPE_INVALID))
391     {
392       for (int i = 0; i < length; i++)
393         devices.push_back(disks[i]);
394
395       dbus_free_string_array(disks);
396     }
397   }
398
399   return devices;
400 }
401
402 void CUDisksProvider::GetDisks(VECSOURCES& devices, bool EnumerateRemovable)
403 {
404   DeviceMap::iterator itr;
405
406   for (itr = m_AvailableDevices.begin(); itr != m_AvailableDevices.end(); ++itr)
407   {
408     CUDiskDevice *device = itr->second;
409     if (device && device->IsApproved() && device->m_isSystemInternal != EnumerateRemovable)
410       devices.push_back(device->ToMediaShare());
411   }
412 }
413 #endif