summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShankar Rao2013-11-04 23:10:48 -0600
committerShankar Rao2013-11-05 14:48:25 -0600
commite4acde5784051be1e02ab23e2fe0fc17bfa24259 (patch)
treedc6118cc935502db39a3e06b32acd68c87ed8c97
parent9c25fe1bb8ab3974b69bcc21930699faf05b365d (diff)
downloaddevice-ti-common-open-e4acde5784051be1e02ab23e2fe0fc17bfa24259.tar.gz
device-ti-common-open-e4acde5784051be1e02ab23e2fe0fc17bfa24259.tar.xz
device-ti-common-open-e4acde5784051be1e02ab23e2fe0fc17bfa24259.zip
USB: MTP HOST: Add service to retrieve MTP objects as a FUSE filesystem.
mtpfuse is a service that is started when an MTP device is plugged in. It enumerates the MTP objects in the peripheral and presents them as a FUSE based filesystem to the host Android framework. In addition, it also kicks off the media scanner so that the media is visible in the gallery. mtpfuse uses Android's' MtpDevice native API to retrieve the MTP objects. Change-Id: Ic9095df8990cb6b911bd225c9834e0dd29e73521 Signed-off-by: Shankar Rao <shankar.nrao@ti.com>
-rw-r--r--mtpfuse/Android.mk28
-rw-r--r--mtpfuse/mtpfuse.cpp478
2 files changed, 506 insertions, 0 deletions
diff --git a/mtpfuse/Android.mk b/mtpfuse/Android.mk
new file mode 100644
index 0000000..f889e53
--- /dev/null
+++ b/mtpfuse/Android.mk
@@ -0,0 +1,28 @@
1ifdef OMAP_ENHANCEMENT
2ifeq ($(TARGET_BOARD_PLATFORM), $(filter $(TARGET_BOARD_PLATFORM), jacinto6))
3
4LOCAL_PATH:= $(call my-dir)
5
6include $(CLEAR_VARS)
7
8LOCAL_CFLAGS:= -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26 -D__MULTI_THREAD
9
10LOCAL_SRC_FILES:= mtpfuse.cpp \
11
12LOCAL_MODULE:= mtpfuse
13LOCAL_CFLAGS := -Wall -Wno-unused-parameter
14
15LOCAL_C_INCLUDES := \
16 $(TOP)/external/libfuse/include \
17 $(LOCAL_PATH)/. \
18 $(TOP)/system/core/include \
19 $(TOP)/frameworks/av/media/mtp \
20 $(TOP)/frameworks/native/include
21
22LOCAL_SHARED_LIBRARIES := libc libusbhost libmtp libutils
23
24LOCAL_STATIC_LIBRARIES := libfuse
25
26include $(BUILD_EXECUTABLE)
27endif # jacinto6
28endif # OMAP_ENHANCEMENT
diff --git a/mtpfuse/mtpfuse.cpp b/mtpfuse/mtpfuse.cpp
new file mode 100644
index 0000000..60db6c4
--- /dev/null
+++ b/mtpfuse/mtpfuse.cpp
@@ -0,0 +1,478 @@
1/*
2 * Copyright (C) 2012 Texas Instruments
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <usbhost/usbhost.h>
18#include <fuse.h>
19#include <MtpStorageInfo.h>
20#include <MtpObjectInfo.h>
21#include <utils/String8.h>
22#include <utils/Vector.h>
23#include <utils/KeyedVector.h>
24#include <stdio.h>
25#include <string.h>
26#include <MtpDevice.h>
27#include <fcntl.h>
28#include <errno.h>
29#include <sys/statfs.h>
30#include <sys/mman.h>
31
32
33#undef FUSE_USE_VERSION
34#define FUSE_USE_VERSION 25
35
36#ifdef DEBUG
37#define STRINGIFY(x) #x
38#define TOSTRING(x) STRINGIFY(x)
39#define DBG(a...) {printf(a);}
40#else
41#define DBG(a...)
42#endif
43
44using namespace android;
45
46MtpDevice* mtp_device = NULL;
47int mtp_device_found = 0;
48MtpStorageIDList* storageAreaList = NULL;
49KeyedVector<uint32_t, char*> storageEntries;
50
51static struct fuse_operations mtpfuse_oper;
52typedef Vector <String8*> PathTokenList;
53typedef Vector <MtpObjectInfo*> childList;
54KeyedVector<uint32_t, childList*> mtpEntries;
55KeyedVector<uint32_t, String8> fileDescriptorTable;
56
57static int usb_device_listed(const char *devname, void* client_data) {
58 int fd;
59 usb_device* device = usb_device_open(devname);
60 if (device == NULL) {
61 DBG("Error opening device\n");
62 return 0;
63 }
64 fd = usb_device_get_fd(device);
65 if (fd < 0) {
66 DBG("Error getting fd\n");
67 return 0;
68 }
69
70 MtpDevice *mtp = MtpDevice::open(devname, fd);
71 if (mtp!= NULL) {
72 DBG("MTP device found\n");
73 mtp_device_found = 1;
74 mtp_device = mtp;
75 }
76 return 0;
77}
78
79static int list_devices () {
80 struct usb_host_context* context = usb_host_init();
81 usb_host_load(context, usb_device_listed, NULL, NULL, NULL);
82 return 0;
83}
84
85static int get_storage_id(PathTokenList* tokens) {
86 String8* storage = tokens->itemAt(0);
87 DBG("Storage %s\n", storage->string());
88 if (storage == NULL) {
89 return -1;
90 }
91 for (size_t i = 0;i < storageEntries.size();i++) {
92 char* desc = storageEntries.valueAt(i);
93 if (strcmp(desc, storage->string()) == 0) {
94 return storageEntries.keyAt(i);
95 }
96 }
97 return -1;
98}
99
100static int mtpfuse_release (const char *path,
101 struct fuse_file_info *fi) {
102 return 0;
103}
104
105static PathTokenList* tokenize_path(String8* path) {
106 String8* remain = path;
107 PathTokenList* tokens = new PathTokenList();
108 do {
109 String8* token = new String8(remain->walkPath(remain));
110 tokens->add(token);
111 } while (*remain != "");
112 return tokens;
113}
114
115static MtpObjectInfo* get_mtp_obj_for_path(const char *path) {
116 PathTokenList* tokens;
117 MtpObjectInfo* ret = NULL;
118 String8* path_str = new String8();
119 path_str->setPathName(path);
120 tokens = tokenize_path(path_str);
121 //Go through mtp entries to get the entries
122 int storageId = get_storage_id(tokens);
123 if (storageId == -1) {
124 DBG("Could not get Storage Id\n");
125 tokens->removeItemsAt(0, tokens->size());
126 delete tokens;
127 delete path_str;
128 return NULL;
129 }
130 int found = 0;
131 int index = mtpEntries.indexOfKey(storageId);
132 childList *list = mtpEntries.valueAt(index);
133 if (list != NULL) {
134 found = 1;
135 }
136 MtpObjectInfo *target = NULL;
137 if (tokens->size() > 1) {
138 found = 0;
139 for (size_t i = 1;i < tokens->size();i++) {
140 for (size_t j = 0;j < list->size();j++) {
141 if (strcmp(list->itemAt(j)->mName, tokens->itemAt(i)->string()) == 0) {
142 uint32_t handle = list->itemAt(j)->mHandle;
143 target = list->itemAt(j);
144 int indx = mtpEntries.indexOfKey(handle);
145 list = NULL;
146 list = mtpEntries.valueAt(indx);
147 if (i == tokens->size() - 1) {
148 found = 1;
149 }
150 break;
151 }
152 }
153 }
154 }
155
156 if (found) {
157 ret = target;
158 }
159 tokens->removeItemsAt(0, tokens->size());
160 delete tokens;
161 delete path_str;
162 return target;
163}
164
165
166static int mtpfuse_open (const char * path, struct fuse_file_info *fi) {
167 MtpObjectInfo *obj = get_mtp_obj_for_path(path);
168 if (obj == NULL) {
169 return -ENOENT;
170 }
171 fi->fh = (uint64_t)obj->mHandle;
172 DBG("Handle is %llu %s\n", fi->fh, obj->mName);
173 return 0;
174}
175
176static int mtpfuse_readdir (const char * path,
177 void *buf, fuse_fill_dir_t filler,
178 off_t offset, struct fuse_file_info *fi) {
179 PathTokenList* tokens;
180 filler (buf, ".", NULL, 0);
181 filler (buf, "..", NULL, 0);
182
183 if (strcmp(path, "/") == 0) {
184 //First get the storages
185 for (size_t i = 0;i < storageEntries.size();i++) {
186 struct stat st;
187 memset (&st, 0, sizeof (st));
188 st.st_nlink = 2;
189 st.st_ino = storageEntries.keyAt(i);
190 st.st_mode = S_IFDIR | 0777;
191 char *desc = storageEntries.valueAt(i);
192 filler (buf, desc, &st, 0);
193 }
194 return 0;
195 } else {
196 String8* path_str = new String8();
197 path_str->setPathName(path);
198 tokens = tokenize_path(path_str);
199 //Go through mtp entries to get the entries
200 int storageId = get_storage_id(tokens);
201 if (storageId == -1) {
202 tokens->removeItemsAt(0, tokens->size());
203 delete tokens;
204 delete path_str;
205 return -ENOENT;
206 }
207 int found = 0;
208 int index = mtpEntries.indexOfKey(storageId);
209 childList *list = mtpEntries.valueAt(index);
210 if (list != NULL) {
211 found = 1;
212 }
213
214 if (tokens->size() > 1) {
215 found = 0;
216 for (size_t i = 1;i < tokens->size();i++) {
217 for (size_t j = 0;j < list->size();j++) {
218 if (strcmp(list->itemAt(j)->mName, tokens->itemAt(i)->string()) == 0) {
219 uint32_t handle = list->itemAt(j)->mHandle;
220 int indx = mtpEntries.indexOfKey(handle);
221 list = NULL;
222 list = mtpEntries.valueAt(indx);
223 if (i == tokens->size() - 1) {
224 found = 1;
225 }
226 break;
227 }
228 }
229 }
230 }
231
232 if (found) {
233 if (list != NULL) {
234 childList *leaves = NULL;
235 for (size_t i = 0;i < list->size();i++) {
236 uint32_t handle = list->itemAt(i)->mHandle;
237 int indx = mtpEntries.indexOfKey(handle);
238 leaves = mtpEntries.valueAt(indx);
239 if (leaves) {
240 if (leaves->size() == 0) {
241 struct stat st;
242 memset (&st, 0, sizeof (st));
243 st.st_ino = list->itemAt(i)->mHandle;
244 st.st_mode = S_IFREG | 0777;
245 filler (buf, list->itemAt(i)->mName, &st, 0);
246 }else {
247 struct stat st;
248 memset (&st, 0, sizeof (st));
249 st.st_ino = list->itemAt(i)->mHandle;
250 st.st_mode = S_IFDIR | 0777;
251 filler (buf, list->itemAt(i)->mName, &st, 0);
252 }
253 }
254 }
255 }
256 }
257 tokens->removeItemsAt(0, tokens->size());
258 delete tokens;
259 delete path_str;
260 }
261 return 0;
262}
263
264
265static int mtpfuse_get_mtp_objects(const char * path) {
266 //First get the storages
267 for (size_t i = 0;i < storageAreaList->size();i++) {
268 uint32_t storageId = storageAreaList->itemAt(i);
269 //Each storage has handle
270 childList *forRoot = new childList;
271 mtpEntries.add(storageId, forRoot);
272 MtpObjectHandleList* mo_list =
273 mtp_device->getObjectHandles(storageId, 0, 0);
274 if (mo_list != NULL) {
275 //Each mtp object handle is associated with a Vector of child nodes.
276 //The object is stored as a KeyedVector which has an associated child Vector
277 for (size_t i = 0;i < mo_list->size();i++) {
278 MtpObjectInfo* info = mtp_device->getObjectInfo
279 (mo_list->itemAt(i));
280 childList *c = new childList;
281 mtpEntries.add(info->mHandle, c);
282 //Handle to childList
283 childList* parent_child_list = NULL;
284 int index = -1;
285 if (info->mParent != 0) {
286 index = mtpEntries.indexOfKey(info->mParent);
287 }else {
288 index = mtpEntries.indexOfKey(storageId);
289 }
290 if (index == -1) {
291 continue;
292 }
293 parent_child_list = mtpEntries.valueAt(index);
294 if (parent_child_list != NULL) {
295 parent_child_list->add(info);
296 }else {
297 DBG("No handle to parent's list\n");
298 }
299 }
300 }
301 }
302 return 0;
303}
304
305static int mtpfuse_getattr (const char * path, struct stat *stbuf) {
306 PathTokenList* tokens;
307
308 memset (stbuf, 0, sizeof (struct stat));
309
310 struct fuse_context *fc;
311 fc = fuse_get_context();
312 stbuf->st_uid = fc->uid;
313 stbuf->st_gid = fc->gid;
314
315 if (strcmp(path, "/") == 0) {
316 stbuf->st_mode = S_IFDIR | 0777;
317 stbuf->st_nlink = 2;
318 return 0;
319
320 } else {
321 String8* path_str = new String8();
322 path_str->setPathName(path);
323 tokens = tokenize_path(path_str);
324 //Go through mtp entries to get the entries
325 int storageId = get_storage_id(tokens);
326 if (storageId == -1) {
327 tokens->removeItemsAt(0, tokens->size());
328 delete tokens;
329 delete path_str;
330 return -ENOENT;
331 }
332 int found = 0;
333 int index = mtpEntries.indexOfKey(storageId);
334 childList *list = mtpEntries.valueAt(index);
335 if (list != NULL) {
336 found = 1;
337 }
338 MtpObjectInfo *target = NULL;
339 if (tokens->size() > 1) {
340 found = 0;
341 for (size_t i = 1;i < tokens->size();i++) {
342 for (size_t j = 0;j < list->size();j++) {
343 if (strcmp(list->itemAt(j)->mName, tokens->itemAt(i)->string()) == 0) {
344 uint32_t handle = list->itemAt(j)->mHandle;
345 target = list->itemAt(j);
346 int indx = mtpEntries.indexOfKey(handle);
347 list = NULL;
348 list = mtpEntries.valueAt(indx);
349 if (i == tokens->size() - 1) {
350 found = 1;
351 }
352 break;
353 }
354 }
355 }
356 }
357 if (found) {
358 if (list != NULL) {
359 //If an object has no child nodes, assume it to be a directory
360 if (list->size() == 0) {
361 DBG("File\n");
362 stbuf->st_ino = target->mHandle;
363 stbuf->st_size = target->mCompressedSize;
364 stbuf->st_blocks = (target->mCompressedSize / 512) +
365 (target->mCompressedSize % 512 > 0 ? 1 : 0);
366 stbuf->st_nlink = 1;
367 stbuf->st_mode = S_IFREG | 0777;
368 stbuf->st_uid = 1000;
369 stbuf->st_gid = 1015;
370 stbuf->st_mtime = target->mDateModified;
371 stbuf->st_ctime = target->mDateModified;
372 stbuf->st_atime = target->mDateModified;
373 }else {
374 DBG("Directory\n");
375 if (target != NULL)
376 stbuf->st_ino = target->mHandle;
377 stbuf->st_mode = S_IFDIR | 0777;
378 stbuf->st_nlink = 2;
379 stbuf->st_uid = 1000;
380 stbuf->st_gid = 1015;
381 }
382 }
383 }
384 tokens->removeItemsAt(0, tokens->size());
385 delete tokens;
386 delete path_str;
387 }
388 return 0;
389}
390
391bool read_callback (void* data, int offset, int length, void* clientData) {
392 //DBG("Received read callback %d %d\n", offset, length);
393 memcpy((char *)clientData + offset, data, length);
394 return true;
395}
396
397static int mtpfuse_read (const char * path, char * buf,
398 size_t size, off_t offset,
399 struct fuse_file_info *fi) {
400 int ret;
401 MtpObjectHandle h = (MtpObjectHandle)fi->fh;
402 MtpObjectInfo *info = mtp_device->getObjectInfo(h);
403 if (info->mCompressedSize < size) {
404 size = info->mCompressedSize;
405 }
406 bool status = mtp_device->readObject(h, read_callback, size, offset, buf);
407 if (status == false) {
408 ret = 0;
409 }else {
410 ret = size;
411 }
412 return ret;
413}
414
415static void start_media_scanner(){ const char* cmd =
416 "am broadcast -a android.intent.action.MEDIA_MOUNTED -d file:///mnt/shell/emulated/0";
417 system(cmd);
418}
419
420void mtpfuse_destroy (void *) {
421 //Release mtp entries
422 for (size_t i = 0;i < mtpEntries.size();i++) {
423 childList* list = mtpEntries.valueAt(i);
424 list->clear();
425 }
426 mtpEntries.clear();
427 mtp_device->close();
428 storageEntries.clear();
429 start_media_scanner();
430}
431
432void * mtpfuse_init (struct fuse_conn_info *conn) {
433 return 0;
434}
435
436int main(int argc, char* argv[]) {
437 int fuse_stat;
438
439 /*Create the mount directory, if it doesn't exist*/
440 struct stat st;
441 if (stat(argv[1], &st) != 0) {
442 if (mkdir(argv[1], S_IRWXU) != 0 && errno != EEXIST) {
443 printf("Could not create mount point %d\n", errno);
444 exit(0);
445 }
446 }
447
448 list_devices();
449 if (mtp_device_found && mtp_device != NULL) {
450 storageAreaList = mtp_device->getStorageIDs();
451 if (storageAreaList == NULL) {
452 return 0;
453 }
454
455 if (storageAreaList->size() == 0) {
456 DBG("No storages found\n");
457 return 0;
458 }
459 DBG("Found %d storage(s)\n", storageAreaList->size());
460 for (size_t i = 0;i < storageAreaList->size();i++) {
461 MtpStorageInfo* storageInfo = mtp_device->getStorageInfo(storageAreaList->itemAt(i));
462 printf("Desc: %s\n", storageInfo->mStorageDescription);
463 storageEntries.add(storageAreaList->itemAt(i), storageInfo->mStorageDescription);
464 }
465 mtpfuse_get_mtp_objects("/");
466 mtpfuse_oper.readdir = mtpfuse_readdir;
467 mtpfuse_oper.release = mtpfuse_release;
468 mtpfuse_oper.getattr = mtpfuse_getattr;
469 mtpfuse_oper.open = mtpfuse_open;
470 mtpfuse_oper.read = mtpfuse_read;
471 mtpfuse_oper.destroy = mtpfuse_destroy;
472 //Mount fuse
473 DBG("Setting up FUSE\n");
474 fuse_stat = fuse_main(argc, argv, &mtpfuse_oper);
475 return fuse_stat;
476 }
477 return 0;
478}