mtp

Merge lp:~cyphermox/mtp/windows+sdcard+security into lp:mtp

Proposed by Mathieu Trudel-Lapierre
Status: Merged
Approved by: Sergio Schvezov
Approved revision: 54
Merged at revision: 45
Proposed branch: lp:~cyphermox/mtp/windows+sdcard+security
Merge into: lp:mtp
Diff against target: 1496 lines (+915/-157)
8 files modified
CMakeLists.txt (+4/-1)
debian/changelog (+26/-0)
debian/control (+3/-0)
debian/mtp-server.conf (+4/-0)
include/MtpDatabase.h (+8/-0)
server/UbuntuMtpDatabase.h (+513/-114)
server/server.cpp (+349/-42)
tests/MockMtpDatabase.h (+8/-0)
To merge this branch: bzr merge lp:~cyphermox/mtp/windows+sdcard+security
Reviewer Review Type Date Requested Status
Sergio Schvezov Approve
PS Jenkins bot continuous-integration Approve
Manuel de la Peña (community) Approve
Michael Frey (community) Approve
Review via email: mp+230834@code.launchpad.net

Commit message

Add support for Windows, SD card, and screen locking.

Description of the change

Add support for Windows, SD card, and screen locking.

To post a comment you must log in.
Revision history for this message
Michael Frey (mfrey) wrote :

Tested on both Mac OS and Ubuntu.

Can confirm security, sdcard support works as intended.

review: Approve
49. By Mathieu Trudel-Lapierre

Add missing 0.0.3+14.04.20140409-0ubuntu2 changelog

50. By Mathieu Trudel-Lapierre

Add missing changes for 0.0.3+14.04.20140409-0ubuntu3.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Sergio Schvezov (sergiusens) wrote :

I'm getting a bunch of these on boot:
terminate called after throwing an instance of 'std::runtime_error'
  what(): org.freedesktop.DBus.Error.NoReply: Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.
I0814 16:34:34.118505 2627 server.cpp:359] MTP server starting...

I'm also not seeing my sdcard; only the internal one (my sdcard has files dropped in, no directories in it)

review: Needs Fixing
Revision history for this message
Mathieu Trudel-Lapierre (cyphermox) wrote :

Are you using the latest image? The security bits depend on having UnityGreeter available on the session bus. It's the only thing that requires DBus.

How do you proceed to do the SD card tests?

Revision history for this message
Sergio Schvezov (sergiusens) wrote :

On viernes 15 de agosto de 2014 11h'33:39 ART, Mathieu Trudel-Lapierre
wrote:
> Are you using the latest image? The security bits depend on
> having UnityGreeter available on the session bus. It's the only
> thing that requires DBus.

- Tried on image 190.

> How do you proceed to do the SD card tests?

- Tried to test with the sdcard plugged on boot
- Tried popping in the sdcard while in the session.

I have music in the sdcard root. The patched mediascanner picks it up just
fine fwiw.

51. By Mathieu Trudel-Lapierre

Fix sdcard hotswapping.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Manuel de la Peña (mandel) wrote :

Some small comments about the code just to make it clear for new commers.

review: Needs Fixing
52. By Mathieu Trudel-Lapierre

Apply suggested changes from review by mandel

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Manuel de la Peña (mandel) :
review: Approve
53. By Mathieu Trudel-Lapierre

Revert use of scoped_ptr; seems to crash at least object listing in some cases.

54. By Mathieu Trudel-Lapierre

Simplify retry logic for when the mountpoint is available but not yet populated.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Sergio Schvezov (sergiusens) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-03-27 15:45:30 +0000
3+++ CMakeLists.txt 2014-08-21 19:42:07 +0000
4@@ -13,7 +13,8 @@
5 set(MTP_VERSION_PATCH 0)
6
7 find_package(Boost REQUIRED COMPONENTS thread system filesystem unit_test_framework)
8-pkg_check_modules(GLOG libglog)
9+pkg_check_modules(DBUSCPP REQUIRED dbus-cpp)
10+pkg_check_modules(GLOG REQUIRED libglog)
11
12 set(
13 MTP_HEADERS
14@@ -59,6 +60,7 @@
15 include/
16 libusbhost/include
17 ${Boost_INCLUDE_DIRS}
18+ ${DBUSCPP_INCLUDE_DIRS}
19 )
20
21 add_library(
22@@ -70,6 +72,7 @@
23 mtpserver
24 android-properties
25 ${GLOG_LIBRARIES}
26+ ${DBUSCPP_LIBRARIES}
27 )
28
29 set_target_properties(
30
31=== modified file 'debian/changelog'
32--- debian/changelog 2014-04-09 03:00:23 +0000
33+++ debian/changelog 2014-08-21 19:42:07 +0000
34@@ -1,3 +1,29 @@
35+mtp (0.0.4+14.04.20140814-0ubuntu1) UNRELEASED; urgency=medium
36+
37+ * New release:
38+ - Bugfixes.
39+ - Enhancements on error handling.
40+ - Add support for working with Windows systems.
41+ - Add support for locking: don't allow access to storage while the screen
42+ is locked.
43+ - Support removable storage by watching for mounted storage in the /media
44+ directory tree.
45+
46+ -- Mathieu Trudel-Lapierre <mathieu-tl@ubuntu.com> Thu, 14 Aug 2014 11:09:02 -0400
47+
48+mtp (0.0.3+14.04.20140409-0ubuntu3) utopic; urgency=medium
49+
50+ * make sure mtp-server only gets started in user sessions and not in lightdm
51+ greeter sessions
52+
53+ -- Oliver Grawert <ogra@ubuntu.com> Tue, 03 Jun 2014 13:24:56 +0200
54+
55+mtp (0.0.3+14.04.20140409-0ubuntu2) utopic; urgency=high
56+
57+ * No change rebuild against boost1.55.
58+
59+ -- Dimitri John Ledkov <xnox@ubuntu.com> Tue, 29 Apr 2014 04:38:54 +0100
60+
61 mtp (0.0.3+14.04.20140409-0ubuntu1) trusty; urgency=medium
62
63 [ Mathieu Trudel-Lapierre ]
64
65=== modified file 'debian/control'
66--- debian/control 2014-03-27 15:45:30 +0000
67+++ debian/control 2014-08-21 19:42:07 +0000
68@@ -12,6 +12,9 @@
69 libboost-test-dev,
70 libandroid-properties-dev,
71 libgoogle-glog-dev,
72+ libdbus-1-dev,
73+ libproperties-cpp-dev,
74+ libdbus-cpp-dev,
75 Standards-Version: 3.9.4
76 Homepage: http://launchpad.net/mtp
77 # If you aren't ~phablet-team but need to upload packaging changes,
78
79=== modified file 'debian/mtp-server.conf'
80--- debian/mtp-server.conf 2014-03-27 15:45:30 +0000
81+++ debian/mtp-server.conf 2014-08-21 19:42:07 +0000
82@@ -3,6 +3,10 @@
83 start on :sys:android-mtp-on or :sys:android-usb-connected
84 stop on desktop-end or :sys:android-mtp-off
85
86+pre-start script
87+ [ "$USER" != "lightdm" ] || { stop; exit 0; }
88+end script
89+
90 env GOOGLE_LOGTOSTDERR=1
91
92 respawn
93
94=== modified file 'include/MtpDatabase.h'
95--- include/MtpDatabase.h 2014-03-27 15:50:32 +0000
96+++ include/MtpDatabase.h 2014-08-21 19:42:07 +0000
97@@ -30,6 +30,14 @@
98 public:
99 virtual ~MtpDatabase() {}
100
101+ // called to add a path to include in the database.
102+ virtual void addStoragePath(const MtpString& path,
103+ MtpStorageID storage,
104+ bool hidden) = 0;
105+
106+ // Called to remove database entries for a storage.
107+ virtual void removeStorage(MtpStorageID storage) = 0;
108+
109 // called from SendObjectInfo to reserve a database entry for the incoming file
110 virtual MtpObjectHandle beginSendObject(const MtpString& path,
111 MtpObjectFormat format,
112
113=== modified file 'server/UbuntuMtpDatabase.h'
114--- server/UbuntuMtpDatabase.h 2014-04-01 15:50:59 +0000
115+++ server/UbuntuMtpDatabase.h 2014-08-21 19:42:07 +0000
116@@ -45,9 +45,12 @@
117 #include <boost/filesystem.hpp>
118 #include <boost/range/adaptors.hpp>
119 #include <boost/range/algorithm.hpp>
120+#include <boost/date_time/posix_time/posix_time.hpp>
121
122 #include <glog/logging.h>
123
124+#define ALL_PROPERTIES 0xffffffff
125+
126 namespace asio = boost::asio;
127 using namespace boost::filesystem;
128
129@@ -64,6 +67,7 @@
130 std::string display_name;
131 std::string path;
132 int watch_fd;
133+ std::time_t last_modified;
134 };
135
136 MtpServer* local_server;
137@@ -85,6 +89,7 @@
138 boost::thread io_service_thread;
139
140 asio::io_service io_svc;
141+ asio::io_service::work work;
142 asio::posix::stream_descriptor stream_desc;
143 asio::streambuf buf;
144 int inotify_fd;
145@@ -113,7 +118,7 @@
146 }
147
148
149- void add_file_entry(path p, MtpObjectHandle parent)
150+ void add_file_entry(path p, MtpObjectHandle parent, MtpStorageID storage)
151 {
152 MtpObjectHandle handle = counter;
153 DbEntry entry;
154@@ -121,28 +126,30 @@
155 counter++;
156
157 if (is_directory(p)) {
158- entry.storage_id = MTP_STORAGE_FIXED_RAM;
159+ entry.storage_id = storage;
160 entry.parent = parent;
161 entry.display_name = std::string(p.filename().string());
162 entry.path = p.string();
163 entry.object_format = MTP_FORMAT_ASSOCIATION;
164 entry.object_size = 0;
165 entry.watch_fd = setup_dir_inotify(p);
166+ entry.last_modified = last_write_time(p);
167
168 db.insert( std::pair<MtpObjectHandle, DbEntry>(handle, entry) );
169
170 if (local_server)
171 local_server->sendObjectAdded(handle);
172
173- parse_directory (p, handle);
174+ parse_directory (p, handle, storage);
175 } else {
176 try {
177- entry.storage_id = MTP_STORAGE_FIXED_RAM;
178+ entry.storage_id = storage;
179 entry.parent = parent;
180 entry.display_name = std::string(p.filename().string());
181 entry.path = p.string();
182 entry.object_format = guess_object_format(p.extension().string());
183 entry.object_size = file_size(p);
184+ entry.last_modified = last_write_time(p);
185
186 VLOG(1) << "Adding \"" << p.string() << "\"";
187
188@@ -157,20 +164,28 @@
189 }
190 }
191
192- void parse_directory(path p, MtpObjectHandle parent)
193+ void parse_directory(path p, MtpObjectHandle parent, MtpStorageID storage)
194 {
195 DbEntry entry;
196 std::vector<path> v;
197-
198- copy(directory_iterator(p), directory_iterator(), std::back_inserter(v));
199+ boost::system::error_code ec;
200+ directory_iterator i (p, ec);
201+
202+ if (ec == boost::system::errc::permission_denied) {
203+ VLOG(2) << "Could not immediately read dir; retrying.";
204+ boost::this_thread::sleep(boost::posix_time::millisec(500));
205+ i = directory_iterator(p);
206+ }
207+
208+ copy(i, directory_iterator(), std::back_inserter(v));
209
210 for (std::vector<path>::const_iterator it(v.begin()), it_end(v.end()); it != it_end; ++it)
211 {
212- add_file_entry(*it, parent);
213+ add_file_entry(*it, parent, storage);
214 }
215 }
216
217- void readFiles(const std::string& sourcedir)
218+ void readFiles(const std::string& sourcedir, MtpStorageID storage, bool hidden)
219 {
220 path p (sourcedir);
221 DbEntry entry;
222@@ -179,20 +194,34 @@
223 try {
224 if (exists(p)) {
225 if (is_directory(p)) {
226- entry.storage_id = MTP_STORAGE_FIXED_RAM;
227- entry.parent = MTP_PARENT_ROOT;
228+ entry.storage_id = storage;
229+ entry.parent = hidden ? MTP_PARENT_ROOT : 0;
230 entry.display_name = std::string(p.filename().string());
231 entry.path = p.string();
232 entry.object_format = MTP_FORMAT_ASSOCIATION;
233 entry.object_size = 0;
234 entry.watch_fd = setup_dir_inotify(p);
235+ entry.last_modified = last_write_time(p);
236
237 db.insert( std::pair<MtpObjectHandle, DbEntry>(handle, entry) );
238
239- parse_directory (p, handle);
240+ parse_directory (p, hidden ? 0 : handle, storage);
241+ } else
242+ LOG(WARNING) << p << " is not a directory.";
243+ } else {
244+ if (storage == MTP_STORAGE_FIXED_RAM)
245+ LOG(WARNING) << p << " does not exist.";
246+ else {
247+ entry.storage_id = storage;
248+ entry.parent = -1;
249+ entry.display_name = std::string(p.parent_path().filename().string());
250+ entry.path = p.parent_path().string();
251+ entry.object_format = MTP_FORMAT_ASSOCIATION;
252+ entry.object_size = 0;
253+ entry.watch_fd = setup_dir_inotify(p.parent_path());
254+ entry.last_modified = 0;
255 }
256- } else
257- LOG(WARNING) << p << " does not exist\n";
258+ }
259 }
260 catch (const filesystem_error& ex) {
261 LOG(ERROR) << ex.what();
262@@ -247,10 +276,22 @@
263 }
264 else if(ievent->len > 0 && ievent->mask & IN_CREATE)
265 {
266+ int parent_handle = parent;
267+
268 VLOG(2) << __PRETTY_FUNCTION__ << ": file created: " << p.string();
269
270+ /* Deal with the special case where the SD card might initially
271+ * require an inotify watch, because it's not yet mounted.
272+ * In this case, the SD card inotify watch is entered as a
273+ * normal object, but the parent for the "real" directory
274+ * for the mounted removable media should be the MTP root
275+ * for the storage ID.
276+ */
277+ if (db.at(parent).parent == MTP_PARENT_ROOT)
278+ parent_handle = 0;
279+
280 /* try to deal with it as if it was a file. */
281- add_file_entry(p, parent);
282+ add_file_entry(p, parent_handle, db.at(parent).storage_id);
283 }
284 else if(ievent->len > 0 && ievent->mask & IN_DELETE)
285 {
286@@ -271,18 +312,18 @@
287 }
288
289 public:
290- UbuntuMtpDatabase(const char *dir):
291+ UbuntuMtpDatabase():
292 counter(1),
293 stream_desc(io_svc),
294+ work(io_svc),
295 buf(1024)
296 {
297- std::string basedir (dir);
298-
299 local_server = nullptr;
300
301 inotify_fd = inotify_init();
302 if (inotify_fd <= 0)
303- PLOG(FATAL) << "Unable to initialize inotify";
304+ PLOG(FATAL) << "Invalid file descriptor to inotify";
305+ VLOG(1) << "using inotify fd " << inotify_fd << " for database";
306
307 stream_desc.assign(inotify_fd);
308
309@@ -291,14 +332,6 @@
310 notifier_thread = boost::thread(&UbuntuMtpDatabase::read_more_notify,
311 this);
312
313- readFiles(basedir + "/Documents");
314- readFiles(basedir + "/Music");
315- readFiles(basedir + "/Videos");
316- readFiles(basedir + "/Pictures");
317- readFiles(basedir + "/Downloads");
318-
319- LOG(INFO) << "Added " << counter << " entries to the database.";
320-
321 io_service_thread = boost::thread(boost::bind(&asio::io_service::run, &io_svc));
322 }
323
324@@ -309,6 +342,22 @@
325 close(inotify_fd);
326 }
327
328+ virtual void addStoragePath(const MtpString& path,
329+ MtpStorageID storage,
330+ bool hidden)
331+ {
332+ readFiles(path, storage, hidden);
333+ }
334+
335+ virtual void removeStorage(MtpStorageID storage)
336+ {
337+ // remove all database entries corresponding to said storage.
338+ BOOST_FOREACH(MtpObjectHandle i, db | boost::adaptors::map_keys) {
339+ if (db.at(i).storage_id == storage)
340+ db.erase(i);
341+ }
342+ }
343+
344 // called from SendObjectInfo to reserve a database entry for the incoming file
345 virtual MtpObjectHandle beginSendObject(
346 const MtpString& path,
347@@ -324,7 +373,8 @@
348 if (parent == 0)
349 return kInvalidObjectHandle;
350
351- VLOG(1) << __PRETTY_FUNCTION__ << ": " << path << " - " << parent;
352+ VLOG(1) << __PRETTY_FUNCTION__ << ": " << path << " - " << parent
353+ << " format: " << std::hex << format << std::dec;
354
355 entry.storage_id = storage;
356 entry.parent = parent;
357@@ -332,6 +382,7 @@
358 entry.path = path;
359 entry.object_format = format;
360 entry.object_size = size;
361+ entry.last_modified = modified;
362
363 db.insert( std::pair<MtpObjectHandle, DbEntry>(handle, entry) );
364
365@@ -351,15 +402,22 @@
366 {
367 VLOG(1) << __PRETTY_FUNCTION__ << ": " << path;
368
369- if (!succeeded) {
370- db.erase(handle);
371- } else {
372- boost::filesystem::path p (path);
373+ try
374+ {
375+ if (!succeeded) {
376+ db.erase(handle);
377+ } else {
378+ boost::filesystem::path p (path);
379
380- if (format != MTP_FORMAT_ASSOCIATION) {
381- /* Resync file size, just in case this is actually an Edit. */
382- db.at(handle).object_size = file_size(p);
383+ if (format != MTP_FORMAT_ASSOCIATION) {
384+ /* Resync file size, just in case this is actually an Edit. */
385+ db.at(handle).object_size = file_size(p);
386+ }
387 }
388+ } catch(...)
389+ {
390+ LOG(ERROR) << __PRETTY_FUNCTION__
391+ << ": failed to complete object creation:" << path;
392 }
393 }
394
395@@ -370,13 +428,18 @@
396 {
397 VLOG(1) << __PRETTY_FUNCTION__ << ": " << storageID << ", " << format << ", " << parent;
398 MtpObjectHandleList* list = nullptr;
399+
400+ if (parent == MTP_PARENT_ROOT)
401+ parent = 0;
402+
403 try
404 {
405 std::vector<MtpObjectHandle> keys;
406
407 BOOST_FOREACH(MtpObjectHandle i, db | boost::adaptors::map_keys) {
408- if (db.at(i).parent == parent)
409- keys.push_back(i);
410+ if (db.at(i).storage_id == storageID && db.at(i).parent == parent)
411+ if (format == 0 || db.at(i).object_format == format)
412+ keys.push_back(i);
413 }
414
415 list = new MtpObjectHandleList(keys);
416@@ -394,14 +457,19 @@
417 MtpObjectHandle parent)
418 {
419 VLOG(1) << __PRETTY_FUNCTION__ << ": " << storageID << ", " << format << ", " << parent;
420+
421+ int result = 0;
422+
423 try
424 {
425- return db.size();
426+ MtpObjectHandleList *list = getObjectList(storageID, format, parent);
427+ result = list->size();
428+ delete list;
429 } catch(...)
430 {
431 }
432
433- return 0;
434+ return result;
435 }
436
437 // callee should delete[] the results from these
438@@ -412,6 +480,22 @@
439 static const MtpObjectFormatList list = {
440 /* Generic files */
441 MTP_FORMAT_UNDEFINED,
442+ MTP_FORMAT_ASSOCIATION, // folders
443+ MTP_FORMAT_TEXT,
444+ MTP_FORMAT_HTML,
445+
446+ /* Supported image formats */
447+ MTP_FORMAT_DEFINED, // generic image
448+ MTP_FORMAT_EXIF_JPEG,
449+ MTP_FORMAT_TIFF_EP,
450+ MTP_FORMAT_BMP,
451+ MTP_FORMAT_GIF,
452+ MTP_FORMAT_JFIF,
453+ MTP_FORMAT_PNG,
454+ MTP_FORMAT_TIFF,
455+ MTP_FORMAT_TIFF_IT,
456+ MTP_FORMAT_JP2,
457+ MTP_FORMAT_JPX,
458
459 /* Supported audio formats */
460 MTP_FORMAT_OGG,
461@@ -455,11 +539,17 @@
462 MTP_PROPERTY_PARENT_OBJECT,
463 MTP_PROPERTY_OBJECT_FORMAT,
464 MTP_PROPERTY_OBJECT_SIZE,
465- MTP_PROPERTY_WIDTH,
466- MTP_PROPERTY_HEIGHT,
467- MTP_PROPERTY_IMAGE_BIT_DEPTH,
468 MTP_PROPERTY_OBJECT_FILE_NAME,
469- MTP_PROPERTY_DISPLAY_NAME
470+ MTP_PROPERTY_DISPLAY_NAME,
471+ MTP_PROPERTY_PERSISTENT_UID,
472+ MTP_PROPERTY_ASSOCIATION_TYPE,
473+ MTP_PROPERTY_ASSOCIATION_DESC,
474+ MTP_PROPERTY_PROTECTION_STATUS,
475+ MTP_PROPERTY_DATE_CREATED,
476+ MTP_PROPERTY_DATE_MODIFIED,
477+ MTP_PROPERTY_HIDDEN,
478+ MTP_PROPERTY_NON_CONSUMABLE,
479+
480 };
481
482 return new MtpObjectPropertyList{list};
483@@ -468,7 +558,10 @@
484 virtual MtpDevicePropertyList* getSupportedDeviceProperties()
485 {
486 VLOG(1) << __PRETTY_FUNCTION__;
487- static const MtpDevicePropertyList list = { MTP_DEVICE_PROPERTY_UNDEFINED };
488+ static const MtpDevicePropertyList list = {
489+ MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
490+ MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
491+ };
492 return new MtpDevicePropertyList{list};
493 }
494
495@@ -477,22 +570,62 @@
496 MtpObjectProperty property,
497 MtpDataPacket& packet)
498 {
499+ char date[20];
500+
501 VLOG(1) << __PRETTY_FUNCTION__
502 << " handle: " << handle
503 << " property: " << MtpDebug::getObjectPropCodeName(property);
504
505- switch(property)
506- {
507- case MTP_PROPERTY_STORAGE_ID: packet.putUInt32(db.at(handle).storage_id); break;
508- case MTP_PROPERTY_PARENT_OBJECT: packet.putUInt32(db.at(handle).parent); break;
509- case MTP_PROPERTY_OBJECT_FORMAT: packet.putUInt32(db.at(handle).object_format); break;
510- case MTP_PROPERTY_OBJECT_SIZE: packet.putUInt32(db.at(handle).object_size); break;
511- case MTP_PROPERTY_DISPLAY_NAME: packet.putString(db.at(handle).display_name.c_str()); break;
512- case MTP_PROPERTY_OBJECT_FILE_NAME: packet.putString(db.at(handle).display_name.c_str()); break;
513- default: return MTP_RESPONSE_GENERAL_ERROR; break;
514- }
515+ if (handle == MTP_PARENT_ROOT || handle == 0)
516+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
517+
518+ try {
519+ switch(property)
520+ {
521+ case MTP_PROPERTY_STORAGE_ID: packet.putUInt32(db.at(handle).storage_id); break;
522+ case MTP_PROPERTY_PARENT_OBJECT: packet.putUInt32(db.at(handle).parent); break;
523+ case MTP_PROPERTY_OBJECT_FORMAT: packet.putUInt16(db.at(handle).object_format); break;
524+ case MTP_PROPERTY_OBJECT_SIZE: packet.putUInt32(db.at(handle).object_size); break;
525+ case MTP_PROPERTY_DISPLAY_NAME: packet.putString(db.at(handle).display_name.c_str()); break;
526+ case MTP_PROPERTY_OBJECT_FILE_NAME: packet.putString(db.at(handle).display_name.c_str()); break;
527+ case MTP_PROPERTY_PERSISTENT_UID: packet.putUInt128(handle); break;
528+ case MTP_PROPERTY_ASSOCIATION_TYPE:
529+ if (db.at(handle).object_format == MTP_FORMAT_ASSOCIATION)
530+ packet.putUInt16(MTP_ASSOCIATION_TYPE_GENERIC_FOLDER);
531+ else
532+ packet.putUInt16(0);
533+ break;
534+ case MTP_PROPERTY_ASSOCIATION_DESC: packet.putUInt32(0); break;
535+ case MTP_PROPERTY_PROTECTION_STATUS:
536+ packet.putUInt16(0x0000); // no files are read-only for now.
537+ break;
538+ case MTP_PROPERTY_DATE_CREATED:
539+ formatDateTime(0, date, sizeof(date));
540+ packet.putString(date);
541+ break;
542+ case MTP_PROPERTY_DATE_MODIFIED:
543+ formatDateTime(db.at(handle).last_modified, date, sizeof(date));
544+ packet.putString(date);
545+ break;
546+ case MTP_PROPERTY_HIDDEN: packet.putUInt16(0); break;
547+ case MTP_PROPERTY_NON_CONSUMABLE: break;
548+ if (db.at(handle).object_format == MTP_FORMAT_ASSOCIATION)
549+ packet.putUInt16(0); // folders are non-consumable
550+ else
551+ packet.putUInt16(1); // files can usually be played.
552+ break;
553+ default: return MTP_RESPONSE_GENERAL_ERROR; break;
554+ }
555
556- return MTP_RESPONSE_OK;
557+ return MTP_RESPONSE_OK;
558+ }
559+ catch (...) {
560+ LOG(ERROR) << __PRETTY_FUNCTION__
561+ << "Could not retrieve property: "
562+ << MtpDebug::getObjectPropCodeName(property)
563+ << " for handle: " << handle;
564+ return MTP_RESPONSE_GENERAL_ERROR;
565+ }
566 }
567
568 virtual MtpResponseCode setObjectPropertyValue(
569@@ -511,6 +644,9 @@
570 << " handle: " << handle
571 << " property: " << MtpDebug::getObjectPropCodeName(property);
572
573+ if (handle == MTP_PARENT_ROOT || handle == 0)
574+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
575+
576 switch(property)
577 {
578 case MTP_PROPERTY_OBJECT_FILE_NAME:
579@@ -539,6 +675,16 @@
580 }
581
582 break;
583+ case MTP_PROPERTY_PARENT_OBJECT:
584+ try {
585+ entry = db.at(handle);
586+ entry.parent = packet.getUInt32();
587+ }
588+ catch (...) {
589+ LOG(ERROR) << "Could not change parent object for handle "
590+ << handle;
591+ return MTP_RESPONSE_GENERAL_ERROR;
592+ }
593 default: return MTP_RESPONSE_OPERATION_NOT_SUPPORTED; break;
594 }
595
596@@ -550,7 +696,16 @@
597 MtpDataPacket& packet)
598 {
599 VLOG(1) << __PRETTY_FUNCTION__;
600- return MTP_RESPONSE_GENERAL_ERROR;
601+ switch(property)
602+ {
603+ case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
604+ case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
605+ packet.putString("");
606+ break;
607+ default: return MTP_RESPONSE_OPERATION_NOT_SUPPORTED; break;
608+ }
609+
610+ return MTP_RESPONSE_OK;
611 }
612
613 virtual MtpResponseCode setDevicePropertyValue(
614@@ -558,14 +713,14 @@
615 MtpDataPacket& packet)
616 {
617 VLOG(1) << __PRETTY_FUNCTION__;
618- return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
619+ return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
620 }
621
622 virtual MtpResponseCode resetDeviceProperty(
623 MtpDeviceProperty property)
624 {
625 VLOG(1) << __PRETTY_FUNCTION__;
626- return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
627+ return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
628 }
629
630 virtual MtpResponseCode getObjectPropertyList(
631@@ -576,8 +731,184 @@
632 int depth,
633 MtpDataPacket& packet)
634 {
635+ std::vector<MtpObjectHandle> handles;
636+
637 VLOG(2) << __PRETTY_FUNCTION__;
638- return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
639+
640+ if (handle == kInvalidObjectHandle)
641+ return MTP_RESPONSE_PARAMETER_NOT_SUPPORTED;
642+
643+ if (property == 0 && groupCode == 0)
644+ return MTP_RESPONSE_PARAMETER_NOT_SUPPORTED;
645+
646+ if (groupCode != 0)
647+ return MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED;
648+
649+ if (depth > 1)
650+ return MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED;
651+
652+ if (depth == 0) {
653+ /* For a depth search, a handle of 0 is valid (objects at the root)
654+ * but it isn't when querying for the properties of a single object.
655+ */
656+ if (db.find(handle) == db.end())
657+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
658+
659+ handles.push_back(handle);
660+ } else {
661+ BOOST_FOREACH(MtpObjectHandle i, db | boost::adaptors::map_keys) {
662+ if (db.at(i).parent == handle)
663+ handles.push_back(i);
664+ }
665+ }
666+
667+ /*
668+ * getObjectPropList returns an ObjectPropList dataset table;
669+ * built as such:
670+ *
671+ * 1- Number of elements (quadruples)
672+ * a1- Element 1 Object Handle
673+ * a2- Element 1 Property Code
674+ * a3- Element 1 Data type
675+ * a4- Element 1 Value
676+ * b... rinse, repeat.
677+ */
678+
679+ if (property == ALL_PROPERTIES)
680+ packet.putUInt32(6 * handles.size());
681+ else
682+ packet.putUInt32(1 * handles.size());
683+
684+ BOOST_FOREACH(MtpObjectHandle i, handles) {
685+ DbEntry entry = db.at(i);
686+
687+ // Persistent Unique Identifier.
688+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_PERSISTENT_UID) {
689+ packet.putUInt32(i);
690+ packet.putUInt16(MTP_PROPERTY_PERSISTENT_UID);
691+ packet.putUInt16(MTP_TYPE_UINT128);
692+ packet.putUInt128(i);
693+ }
694+
695+ // Storage ID
696+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_STORAGE_ID) {
697+ packet.putUInt32(i);
698+ packet.putUInt16(MTP_PROPERTY_STORAGE_ID);
699+ packet.putUInt16(MTP_TYPE_UINT32);
700+ packet.putUInt32(entry.storage_id);
701+ }
702+
703+ // Parent
704+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_PARENT_OBJECT) {
705+ packet.putUInt32(i);
706+ packet.putUInt16(MTP_PROPERTY_PARENT_OBJECT);
707+ packet.putUInt16(MTP_TYPE_UINT32);
708+ packet.putUInt32(entry.parent);
709+ }
710+
711+ // Object Format
712+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_OBJECT_FORMAT) {
713+ packet.putUInt32(i);
714+ packet.putUInt16(MTP_PROPERTY_OBJECT_FORMAT);
715+ packet.putUInt16(MTP_TYPE_UINT16);
716+ packet.putUInt16(entry.object_format);
717+ }
718+
719+ // Object Size
720+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_OBJECT_SIZE) {
721+ packet.putUInt32(i);
722+ packet.putUInt16(MTP_PROPERTY_OBJECT_SIZE);
723+ packet.putUInt16(MTP_TYPE_UINT32);
724+ packet.putUInt32(entry.object_size);
725+ }
726+
727+ // Object File Name
728+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_OBJECT_FILE_NAME) {
729+ packet.putUInt32(i);
730+ packet.putUInt16(MTP_PROPERTY_OBJECT_FILE_NAME);
731+ packet.putUInt16(MTP_TYPE_STR);
732+ packet.putString(entry.display_name.c_str());
733+ }
734+
735+ // Display Name
736+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_DISPLAY_NAME) {
737+ packet.putUInt32(i);
738+ packet.putUInt16(MTP_PROPERTY_DISPLAY_NAME);
739+ packet.putUInt16(MTP_TYPE_STR);
740+ packet.putString(entry.display_name.c_str());
741+ }
742+
743+ // Association Type
744+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_ASSOCIATION_TYPE) {
745+ packet.putUInt32(i);
746+ packet.putUInt16(MTP_PROPERTY_ASSOCIATION_TYPE);
747+ packet.putUInt16(MTP_TYPE_UINT16);
748+ if (entry.object_format == MTP_FORMAT_ASSOCIATION)
749+ packet.putUInt16(MTP_ASSOCIATION_TYPE_GENERIC_FOLDER);
750+ else
751+ packet.putUInt16(0);
752+ }
753+
754+ // Association Description
755+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_ASSOCIATION_DESC) {
756+ packet.putUInt32(i);
757+ packet.putUInt16(MTP_PROPERTY_ASSOCIATION_DESC);
758+ packet.putUInt16(MTP_TYPE_UINT32);
759+ packet.putUInt32(0);
760+ }
761+
762+ // Protection Status
763+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_PROTECTION_STATUS) {
764+ packet.putUInt32(i);
765+ packet.putUInt16(MTP_PROPERTY_PROTECTION_STATUS);
766+ packet.putUInt16(MTP_TYPE_UINT16);
767+ packet.putUInt16(0x0000); //FIXME: all files are read-write for now
768+ // packet.putUInt16(0x8001);
769+ }
770+
771+ // Date Created
772+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_DATE_CREATED) {
773+ char date[20];
774+ formatDateTime(0, date, sizeof(date));
775+ packet.putUInt32(i);
776+ packet.putUInt16(MTP_PROPERTY_DATE_CREATED);
777+ packet.putUInt16(MTP_TYPE_STR);
778+ packet.putString(date);
779+ }
780+
781+ // Date Modified
782+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_DATE_MODIFIED) {
783+ char date[20];
784+ formatDateTime(entry.last_modified, date, sizeof(date));
785+ packet.putUInt32(i);
786+ packet.putUInt16(MTP_PROPERTY_DATE_CREATED);
787+ packet.putUInt16(MTP_TYPE_STR);
788+ packet.putString(date);
789+ }
790+
791+ // Hidden
792+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_HIDDEN) {
793+ packet.putUInt32(i);
794+ packet.putUInt16(MTP_PROPERTY_HIDDEN);
795+ packet.putUInt16(MTP_TYPE_UINT16);
796+ packet.putUInt16(0);
797+ }
798+
799+ // Non Consumable
800+ if (property == ALL_PROPERTIES || property == MTP_PROPERTY_NON_CONSUMABLE) {
801+ packet.putUInt32(i);
802+ packet.putUInt16(MTP_PROPERTY_NON_CONSUMABLE);
803+ packet.putUInt16(MTP_TYPE_UINT16);
804+ if (entry.object_format == MTP_FORMAT_ASSOCIATION)
805+ packet.putUInt16(0); // folders are non-consumable
806+ else
807+ packet.putUInt16(1); // files can usually be played.
808+ break;
809+ }
810+
811+ }
812+
813+ return MTP_RESPONSE_OK;
814 }
815
816 virtual MtpResponseCode getObjectInfo(
817@@ -586,27 +917,37 @@
818 {
819 VLOG(2) << __PRETTY_FUNCTION__;
820
821- info.mHandle = handle;
822- info.mStorageID = db.at(handle).storage_id;
823- info.mFormat = db.at(handle).object_format;
824- info.mProtectionStatus = 0x0;
825- info.mCompressedSize = db.at(handle).object_size;
826- info.mImagePixWidth = 0;
827- info.mImagePixHeight = 0;
828- info.mImagePixDepth = 0;
829- info.mParent = db.at(handle).parent;
830- info.mAssociationType = 0;
831- info.mAssociationDesc = 0;
832- info.mSequenceNumber = 0;
833- info.mName = ::strdup(db.at(handle).display_name.c_str());
834- info.mDateCreated = 0;
835- info.mDateModified = 0;
836- info.mKeywords = ::strdup("ubuntu,touch");
837-
838- if (VLOG_IS_ON(2))
839- info.print();
840-
841- return MTP_RESPONSE_OK;
842+ if (handle == 0 || handle == MTP_PARENT_ROOT)
843+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
844+
845+ try {
846+ info.mHandle = handle;
847+ info.mStorageID = db.at(handle).storage_id;
848+ info.mFormat = db.at(handle).object_format;
849+ info.mProtectionStatus = 0x0;
850+ info.mCompressedSize = db.at(handle).object_size;
851+ info.mImagePixWidth = 0;
852+ info.mImagePixHeight = 0;
853+ info.mImagePixDepth = 0;
854+ info.mParent = db.at(handle).parent;
855+ info.mAssociationType
856+ = info.mFormat == MTP_FORMAT_ASSOCIATION
857+ ? MTP_ASSOCIATION_TYPE_GENERIC_FOLDER : 0;
858+ info.mAssociationDesc = 0;
859+ info.mSequenceNumber = 0;
860+ info.mName = ::strdup(db.at(handle).display_name.c_str());
861+ info.mDateCreated = 0;
862+ info.mDateModified = db.at(handle).last_modified;
863+ info.mKeywords = ::strdup("ubuntu,touch");
864+
865+ if (VLOG_IS_ON(2))
866+ info.print();
867+
868+ return MTP_RESPONSE_OK;
869+ }
870+ catch (...) {
871+ return MTP_RESPONSE_GENERAL_ERROR;
872+ }
873 }
874
875 virtual void* getThumbnail(MtpObjectHandle handle, size_t& outThumbSize)
876@@ -625,15 +966,29 @@
877 int64_t& outFileLength,
878 MtpObjectFormat& outFormat)
879 {
880- DbEntry entry = db.at(handle);
881-
882 VLOG(1) << __PRETTY_FUNCTION__ << " handle: " << handle;
883
884- outFilePath = std::string(entry.path);
885- outFileLength = entry.object_size;
886- outFormat = entry.object_format;
887-
888- return MTP_RESPONSE_OK;
889+ if (handle == 0 || handle == MTP_PARENT_ROOT)
890+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
891+
892+ try {
893+ DbEntry entry = db.at(handle);
894+
895+ VLOG(2) << __PRETTY_FUNCTION__
896+ << "handle: " << handle
897+ << "path: " << entry.path
898+ << "length: " << entry.object_size
899+ << "format: " << entry.object_format;
900+
901+ outFilePath = std::string(entry.path);
902+ outFileLength = entry.object_size;
903+ outFormat = entry.object_format;
904+
905+ return MTP_RESPONSE_OK;
906+ }
907+ catch (...) {
908+ return MTP_RESPONSE_GENERAL_ERROR;
909+ }
910 }
911
912 virtual MtpResponseCode deleteFile(MtpObjectHandle handle)
913@@ -643,25 +998,33 @@
914
915 VLOG(2) << __PRETTY_FUNCTION__ << " handle: " << handle;
916
917- if (db.at(handle).object_format == MTP_FORMAT_ASSOCIATION)
918- inotify_rm_watch(inotify_fd, db.at(handle).watch_fd);
919-
920- new_size = db.erase(handle);
921-
922- if (orig_size > new_size) {
923- /* Recursively remove children object from the DB as well.
924- * we can safely ignore failures here, since the objects
925- * would not be reachable anyway.
926- */
927- BOOST_FOREACH(MtpObjectHandle i, db | boost::adaptors::map_keys) {
928- if (db.at(i).parent == handle)
929- db.erase(i);
930+ if (handle == 0 || handle == MTP_PARENT_ROOT)
931+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
932+
933+ try {
934+ if (db.at(handle).object_format == MTP_FORMAT_ASSOCIATION)
935+ inotify_rm_watch(inotify_fd, db.at(handle).watch_fd);
936+
937+ new_size = db.erase(handle);
938+
939+ if (orig_size > new_size) {
940+ /* Recursively remove children object from the DB as well.
941+ * we can safely ignore failures here, since the objects
942+ * would not be reachable anyway.
943+ */
944+ BOOST_FOREACH(MtpObjectHandle i, db | boost::adaptors::map_keys) {
945+ if (db.at(i).parent == handle)
946+ db.erase(i);
947+ }
948+
949+ return MTP_RESPONSE_OK;
950 }
951-
952- return MTP_RESPONSE_OK;
953+ else
954+ return MTP_RESPONSE_GENERAL_ERROR;
955 }
956- else
957+ catch (...) {
958 return MTP_RESPONSE_GENERAL_ERROR;
959+ }
960 }
961
962 virtual MtpResponseCode moveFile(MtpObjectHandle handle, MtpObjectHandle new_parent)
963@@ -669,8 +1032,16 @@
964 VLOG(1) << __PRETTY_FUNCTION__ << " handle: " << handle
965 << " new parent: " << new_parent;
966
967- // change parent
968- db.at(handle).parent = new_parent;
969+ if (handle == 0 || handle == MTP_PARENT_ROOT)
970+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
971+
972+ try {
973+ // change parent
974+ db.at(handle).parent = new_parent;
975+ }
976+ catch (...) {
977+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
978+ }
979
980 return MTP_RESPONSE_OK;
981 }
982@@ -690,7 +1061,13 @@
983 virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle handle)
984 {
985 VLOG(1) << __PRETTY_FUNCTION__;
986- return nullptr;
987+
988+ if (handle == 0 || handle == MTP_PARENT_ROOT)
989+ return nullptr;
990+
991+ return getObjectList(db.at(handle).storage_id,
992+ handle,
993+ db.at(handle).object_format);
994 }
995
996 virtual MtpResponseCode setObjectReferences(
997@@ -698,7 +1075,10 @@
998 MtpObjectHandleList* references)
999 {
1000 VLOG(1) << __PRETTY_FUNCTION__;
1001- return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
1002+
1003+ // ignore, we don't keep the references in a list.
1004+
1005+ return MTP_RESPONSE_OK;
1006 }
1007
1008 virtual MtpProperty* getObjectPropertyDesc(
1009@@ -710,14 +1090,23 @@
1010 MtpProperty* result = nullptr;
1011 switch(property)
1012 {
1013- case MTP_PROPERTY_STORAGE_ID: result = new MtpProperty(property, MTP_TYPE_UINT32); break;
1014- case MTP_PROPERTY_OBJECT_FORMAT: result = new MtpProperty(property, MTP_TYPE_UINT32); break;
1015- case MTP_PROPERTY_OBJECT_SIZE: result = new MtpProperty(property, MTP_TYPE_UINT32); break;
1016- case MTP_PROPERTY_WIDTH: result = new MtpProperty(property, MTP_TYPE_UINT32); break;
1017- case MTP_PROPERTY_HEIGHT: result = new MtpProperty(property, MTP_TYPE_UINT32); break;
1018- case MTP_PROPERTY_IMAGE_BIT_DEPTH: result = new MtpProperty(property, MTP_TYPE_UINT32); break;
1019+ case MTP_PROPERTY_STORAGE_ID: result = new MtpProperty(property, MTP_TYPE_UINT32, false); break;
1020+ case MTP_PROPERTY_PARENT_OBJECT: result = new MtpProperty(property, MTP_TYPE_UINT32, true); break;
1021+ case MTP_PROPERTY_OBJECT_FORMAT: result = new MtpProperty(property, MTP_TYPE_UINT16, false); break;
1022+ case MTP_PROPERTY_OBJECT_SIZE: result = new MtpProperty(property, MTP_TYPE_UINT32, false); break;
1023+ case MTP_PROPERTY_WIDTH: result = new MtpProperty(property, MTP_TYPE_UINT32, false); break;
1024+ case MTP_PROPERTY_HEIGHT: result = new MtpProperty(property, MTP_TYPE_UINT32, false); break;
1025+ case MTP_PROPERTY_IMAGE_BIT_DEPTH: result = new MtpProperty(property, MTP_TYPE_UINT32, false); break;
1026 case MTP_PROPERTY_DISPLAY_NAME: result = new MtpProperty(property, MTP_TYPE_STR, true); break;
1027 case MTP_PROPERTY_OBJECT_FILE_NAME: result = new MtpProperty(property, MTP_TYPE_STR, true); break;
1028+ case MTP_PROPERTY_PERSISTENT_UID: result = new MtpProperty(property, MTP_TYPE_UINT128, false); break;
1029+ case MTP_PROPERTY_ASSOCIATION_TYPE: result = new MtpProperty(property, MTP_TYPE_UINT16, false); break;
1030+ case MTP_PROPERTY_ASSOCIATION_DESC: result = new MtpProperty(property, MTP_TYPE_UINT32, false); break;
1031+ case MTP_PROPERTY_PROTECTION_STATUS: result = new MtpProperty(property, MTP_TYPE_UINT16, false); break;
1032+ case MTP_PROPERTY_DATE_CREATED: result = new MtpProperty(property, MTP_TYPE_STR, false); break;
1033+ case MTP_PROPERTY_DATE_MODIFIED: result = new MtpProperty(property, MTP_TYPE_STR, false); break;
1034+ case MTP_PROPERTY_HIDDEN: result = new MtpProperty(property, MTP_TYPE_UINT16, false); break;
1035+ case MTP_PROPERTY_NON_CONSUMABLE: result = new MtpProperty(property, MTP_TYPE_UINT16, false); break;
1036 default: break;
1037 }
1038
1039@@ -727,7 +1116,17 @@
1040 virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property)
1041 {
1042 VLOG(1) << __PRETTY_FUNCTION__ << MtpDebug::getDevicePropCodeName(property);
1043- return new MtpProperty(MTP_DEVICE_PROPERTY_UNDEFINED, MTP_TYPE_UNDEFINED);
1044+
1045+ MtpProperty* result = nullptr;
1046+ switch(property)
1047+ {
1048+ case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
1049+ case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
1050+ result = new MtpProperty(property, MTP_TYPE_STR, false); break;
1051+ default: break;
1052+ }
1053+
1054+ return result;
1055 }
1056
1057 virtual void sessionStarted(MtpServer* server)
1058
1059=== modified file 'server/server.cpp'
1060--- server/server.cpp 2014-03-27 15:45:30 +0000
1061+++ server/server.cpp 2014-08-21 19:42:07 +0000
1062@@ -32,6 +32,69 @@
1063 #include <hybris/properties/properties.h>
1064 #include <glog/logging.h>
1065
1066+#include <core/dbus/bus.h>
1067+#include <core/dbus/object.h>
1068+#include <core/dbus/property.h>
1069+#include <core/dbus/service.h>
1070+#include <core/dbus/signal.h>
1071+
1072+#include <core/dbus/asio/executor.h>
1073+#include <core/dbus/types/stl/tuple.h>
1074+#include <core/dbus/types/stl/vector.h>
1075+#include <core/dbus/types/struct.h>
1076+
1077+namespace dbus = core::dbus;
1078+using namespace android;
1079+
1080+namespace core
1081+{
1082+dbus::Bus::Ptr the_session_bus()
1083+{
1084+ static dbus::Bus::Ptr session_bus = std::make_shared<dbus::Bus>(dbus::WellKnownBus::session);
1085+ return session_bus;
1086+}
1087+
1088+struct UnityGreeter
1089+{
1090+ struct Properties
1091+ {
1092+ struct IsActive
1093+ {
1094+ inline static std::string name()
1095+ {
1096+ return "IsActive";
1097+ };
1098+ typedef UnityGreeter Interface;
1099+ typedef bool ValueType;
1100+ static const bool readable = true;
1101+ static const bool writable = false;
1102+ };
1103+ };
1104+};
1105+}
1106+
1107+namespace core
1108+{
1109+namespace dbus
1110+{
1111+namespace traits
1112+{
1113+template<>
1114+struct Service<core::UnityGreeter>
1115+{
1116+ inline static const std::string& interface_name()
1117+ {
1118+ static const std::string s
1119+ {
1120+ "com.canonical.UnityGreeter"
1121+ };
1122+ return s;
1123+ }
1124+};
1125+}
1126+}
1127+}
1128+
1129 namespace
1130 {
1131 struct FileSystemConfig
1132@@ -40,59 +103,303 @@
1133 static const int directory_perm = 0755;
1134 };
1135
1136-android::MtpStorage* home_storage;
1137-
1138 }
1139
1140+class MtpDaemon
1141+{
1142+
1143+private:
1144+ struct passwd *userdata;
1145+
1146+ // Mtp stuff
1147+ MtpServer* server;
1148+ MtpStorage* home_storage;
1149+ MtpStorage* sd_card;
1150+ MtpDatabase* mtp_database;
1151+
1152+ // Security
1153+ std::shared_ptr<core::dbus::Property<core::UnityGreeter::Properties::IsActive> > is_active;
1154+
1155+ // inotify stuff
1156+ boost::thread notifier_thread;
1157+ boost::thread io_service_thread;
1158+
1159+ asio::io_service io_svc;
1160+ asio::io_service::work work;
1161+ asio::posix::stream_descriptor stream_desc;
1162+ asio::streambuf buf;
1163+
1164+ int inotify_fd;
1165+
1166+ int watch_fd;
1167+ int media_fd;
1168+
1169+ // storage
1170+ std::map<std::string, std::tuple<MtpStorage*, bool> > removables;
1171+ bool home_storage_added;
1172+
1173+ void add_removable_storage(const char *path, const char *name)
1174+ {
1175+ static int storageID = MTP_STORAGE_REMOVABLE_RAM;
1176+
1177+ MtpStorage *removable = new MtpStorage(
1178+ storageID,
1179+ path,
1180+ name,
1181+ 1024 * 1024 * 100, /* 100 MB reserved space, to avoid filling the disk */
1182+ true,
1183+ 1024 * 1024 * 1024 * 2 /* 2GB arbitrary max file size */);
1184+
1185+ storageID++;
1186+
1187+ bool screen_locked = is_active->get();
1188+
1189+ if (!screen_locked) {
1190+ mtp_database->addStoragePath(path,
1191+ removable->getStorageID(),
1192+ true);
1193+ server->addStorage(removable);
1194+ }
1195+
1196+ removables.insert(std::pair<std::string, std::tuple<MtpStorage*, bool> >
1197+ (name,
1198+ std::make_tuple(removable,
1199+ screen_locked ? false : true)));
1200+ }
1201+
1202+ void add_mountpoint_watch(const std::string& path)
1203+ {
1204+ VLOG(1) << "Adding notify watch for " << path;
1205+ watch_fd = inotify_add_watch(inotify_fd,
1206+ path.c_str(),
1207+ IN_CREATE | IN_DELETE);
1208+ }
1209+
1210+ void read_more_notify()
1211+ {
1212+ VLOG(1) << __PRETTY_FUNCTION__;
1213+
1214+ stream_desc.async_read_some(buf.prepare(buf.max_size()),
1215+ boost::bind(&MtpDaemon::inotify_handler,
1216+ this,
1217+ asio::placeholders::error,
1218+ asio::placeholders::bytes_transferred));
1219+ }
1220+
1221+ void inotify_handler(const boost::system::error_code&,
1222+ std::size_t transferred)
1223+ {
1224+ size_t processed = 0;
1225+
1226+ while(transferred - processed >= sizeof(inotify_event))
1227+ {
1228+ const char* cdata = processed + asio::buffer_cast<const char*>(buf.data());
1229+ const inotify_event* ievent = reinterpret_cast<const inotify_event*>(cdata);
1230+ path storage_path ("/media");
1231+
1232+ processed += sizeof(inotify_event) + ievent->len;
1233+
1234+ storage_path /= userdata->pw_name;
1235+
1236+ if (ievent->len > 0 && ievent->mask & IN_CREATE)
1237+ {
1238+ if (ievent->wd == media_fd) {
1239+ VLOG(1) << "media root was created for user " << ievent->name;
1240+ add_mountpoint_watch(storage_path.string());
1241+ } else {
1242+ VLOG(1) << "Storage was added: " << ievent->name;
1243+ storage_path /= ievent->name;
1244+ add_removable_storage(storage_path.string().c_str(), ievent->name);
1245+ }
1246+ }
1247+ else if (ievent->len > 0 && ievent->mask & IN_DELETE)
1248+ {
1249+ VLOG(1) << "Storage was removed: " << ievent->name;
1250+
1251+ // Try to match to which storage was removed.
1252+ BOOST_FOREACH(std::string name, removables | boost::adaptors::map_keys) {
1253+ if (name == ievent->name) {
1254+ auto t = removables.at(name);
1255+ MtpStorage *storage = std::get<0>(t);
1256+
1257+ VLOG(2) << "removing storage id "
1258+ << storage->getStorageID();
1259+
1260+ server->removeStorage(storage);
1261+ mtp_database->removeStorage(storage->getStorageID());
1262+ }
1263+ }
1264+ }
1265+ }
1266+
1267+ read_more_notify();
1268+ }
1269+
1270+public:
1271+
1272+ MtpDaemon(int fd):
1273+ stream_desc(io_svc),
1274+ work(io_svc),
1275+ buf(1024)
1276+ {
1277+ userdata = getpwuid (getuid());
1278+
1279+ // Removable storage hacks
1280+ inotify_fd = inotify_init();
1281+ if (inotify_fd <= 0)
1282+ PLOG(FATAL) << "Unable to initialize inotify";
1283+ VLOG(1) << "using inotify fd " << inotify_fd << " for daemon";
1284+
1285+ stream_desc.assign(inotify_fd);
1286+ notifier_thread = boost::thread(&MtpDaemon::read_more_notify, this);
1287+ io_service_thread = boost::thread(boost::bind(&asio::io_service::run, &io_svc));
1288+
1289+
1290+ // MTP database.
1291+ mtp_database = new UbuntuMtpDatabase();
1292+
1293+
1294+ // MTP server
1295+ server = new MtpServer(
1296+ fd,
1297+ mtp_database,
1298+ false,
1299+ userdata->pw_gid,
1300+ FileSystemConfig::file_perm,
1301+ FileSystemConfig::directory_perm);
1302+
1303+ // security / screen locking
1304+ auto bus = core::the_session_bus();
1305+ bus->install_executor(core::dbus::asio::make_executor(bus));
1306+ std::thread t {std::bind(&dbus::Bus::run, bus)};
1307+ auto greeter_service = dbus::Service::use_service(bus, "com.canonical.UnityGreeter");
1308+ dbus::Object::Ptr greeter = greeter_service->object_for_path(dbus::types::ObjectPath("/"));
1309+
1310+ t.detach();
1311+
1312+ is_active = greeter->get_property<core::UnityGreeter::Properties::IsActive>();
1313+ }
1314+
1315+ void initStorage()
1316+ {
1317+ char product_name[PROP_VALUE_MAX];
1318+
1319+ // Local storage
1320+ property_get ("ro.product.model", product_name, "Ubuntu Touch device");
1321+
1322+ home_storage = new MtpStorage(
1323+ MTP_STORAGE_FIXED_RAM,
1324+ userdata->pw_dir,
1325+ product_name,
1326+ 1024 * 1024 * 100, /* 100 MB reserved space, to avoid filling the disk */
1327+ false,
1328+ 1024 * 1024 * 1024 * 2 /* 2GB arbitrary max file size */);
1329+ mtp_database->addStoragePath(std::string(userdata->pw_dir) + "/Documents",
1330+ MTP_STORAGE_FIXED_RAM, false);
1331+ mtp_database->addStoragePath(std::string(userdata->pw_dir) + "/Music",
1332+ MTP_STORAGE_FIXED_RAM, false);
1333+ mtp_database->addStoragePath(std::string(userdata->pw_dir) + "/Videos",
1334+ MTP_STORAGE_FIXED_RAM, false);
1335+ mtp_database->addStoragePath(std::string(userdata->pw_dir) + "/Pictures",
1336+ MTP_STORAGE_FIXED_RAM, false);
1337+ mtp_database->addStoragePath(std::string(userdata->pw_dir) + "/Downloads",
1338+ MTP_STORAGE_FIXED_RAM, false);
1339+ home_storage_added = false;
1340+
1341+ // Get any already-mounted removable storage.
1342+ path p(std::string("/media/") + userdata->pw_name);
1343+ if (exists(p)) {
1344+ std::vector<path> v;
1345+ copy(directory_iterator(p), directory_iterator(), std::back_inserter(v));
1346+ for (std::vector<path>::const_iterator it(v.begin()), it_end(v.end()); it != it_end; ++it)
1347+ {
1348+ add_removable_storage(it->string().c_str(), it->leaf().c_str());
1349+ }
1350+
1351+ // make sure we can catch any new removable storage that gets added.
1352+ add_mountpoint_watch(p.string());
1353+ } else {
1354+ media_fd = inotify_add_watch(inotify_fd,
1355+ "/media",
1356+ IN_CREATE | IN_DELETE);
1357+ }
1358+
1359+ }
1360+
1361+ ~MtpDaemon()
1362+ {
1363+ // Cleanup
1364+ inotify_rm_watch(inotify_fd, watch_fd);
1365+ io_svc.stop();
1366+ notifier_thread.detach();
1367+ io_service_thread.join();
1368+ close(inotify_fd);
1369+ }
1370+
1371+ void run()
1372+ {
1373+ if (is_active->get()) {
1374+ is_active->changed().connect([this](bool active)
1375+ {
1376+ if (!active) {
1377+ VLOG(2) << "device was unlocked, adding storage";
1378+ if (home_storage && !home_storage_added) {
1379+ server->addStorage(home_storage);
1380+ home_storage_added = true;
1381+ }
1382+ BOOST_FOREACH(std::string name, removables | boost::adaptors::map_keys) {
1383+ auto t = removables.at(name);
1384+ MtpStorage *storage = std::get<0>(t);
1385+ bool added = std::get<1>(t);
1386+ if (!added) {
1387+ mtp_database->addStoragePath(storage->getPath(),
1388+ storage->getStorageID(),
1389+ true);
1390+ server->addStorage(storage);
1391+ }
1392+ }
1393+ }
1394+ });
1395+ } else {
1396+ VLOG(2) << "device is not locked, adding storage";
1397+ if (home_storage) {
1398+ server->addStorage(home_storage);
1399+ home_storage_added = true;
1400+ }
1401+ BOOST_FOREACH(std::string name, removables | boost::adaptors::map_keys) {
1402+ auto t = removables.at(name);
1403+ MtpStorage *storage = std::get<0>(t);
1404+ bool added = std::get<1>(t);
1405+ if (!added) {
1406+ mtp_database->addStoragePath(storage->getPath(),
1407+ storage->getStorageID(),
1408+ true);
1409+ server->addStorage(storage);
1410+ }
1411+ }
1412+ }
1413+
1414+ // start the MtpServer main loop
1415+ server->run();
1416+ }
1417+};
1418+
1419 int main(int argc, char** argv)
1420 {
1421- struct passwd *userdata = getpwuid (getuid());
1422- char product_name[PROP_VALUE_MAX];
1423- int fd = open("/dev/mtp_usb", O_RDWR);
1424-
1425 google::InitGoogleLogging(argv[0]);
1426
1427 LOG(INFO) << "MTP server starting...";
1428
1429+ int fd = open("/dev/mtp_usb", O_RDWR);
1430 if (fd < 0)
1431 {
1432 LOG(FATAL) << "Error opening /dev/mtp_usb, aborting now...";
1433 }
1434
1435- std::shared_ptr<android::MtpServer> server
1436- {
1437- new android::MtpServer(
1438- fd,
1439- new android::UbuntuMtpDatabase(userdata->pw_dir),
1440- false,
1441- userdata->pw_gid,
1442- FileSystemConfig::file_perm,
1443- FileSystemConfig::directory_perm)
1444- };
1445-
1446- property_get ("ro.product.model", product_name, "Ubuntu Touch device");
1447-
1448- home_storage = new android::MtpStorage(
1449- MTP_STORAGE_FIXED_RAM,
1450- userdata->pw_dir,
1451- product_name,
1452- 1024 * 1024 * 100, /* 100 MB reserved space, to avoid filling the disk */
1453- false,
1454- 1024 * 1024 * 1024 * 2 /* 2GB arbitrary max file size */);
1455-
1456- server->addStorage(home_storage);
1457- server->run();
1458-
1459- /*
1460- std::thread t{[&server](){ server->run(); }};
1461-
1462- sigset_t signal_set;
1463- sigemptyset(&signal_set);
1464- sigaddset(&signal_set, SIGINT);
1465- int signal;
1466- sigwait(&signal_set, &signal);
1467-
1468- if (t.joinable())
1469- t.join();
1470- */
1471+ MtpDaemon *d = new MtpDaemon(fd);
1472+
1473+ d->initStorage();
1474+ d->run();
1475+
1476+ delete d;
1477 }
1478
1479=== modified file 'tests/MockMtpDatabase.h'
1480--- tests/MockMtpDatabase.h 2014-03-27 15:50:32 +0000
1481+++ tests/MockMtpDatabase.h 2014-08-21 19:42:07 +0000
1482@@ -74,6 +74,14 @@
1483
1484 virtual ~MockMtpDatabase() {}
1485
1486+ virtual void addStoragePath(const MtpString& path, MtpStorageID storage, bool hidden)
1487+ {
1488+ }
1489+
1490+ virtual void removeStorage(MtpStorageID storage)
1491+ {
1492+ }
1493+
1494 virtual MtpObjectHandle beginSendObject(
1495 const MtpString& path,
1496 MtpObjectFormat format,

Subscribers

People subscribed via source and target branches