android_mtp_MtpServer.cpp revision aa48835ce9a84d9d587d7a4b10ee6ea7c00e00ec
1/*
2 * Copyright (C) 2010 The Android Open Source Project
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#define LOG_TAG "MtpServerJNI"
18#include "utils/Log.h"
19
20#include <stdio.h>
21#include <assert.h>
22#include <limits.h>
23#include <unistd.h>
24#include <fcntl.h>
25#include <sys/ioctl.h>
26#include <utils/threads.h>
27
28#ifdef HAVE_ANDROID_OS
29#include <linux/usb/f_mtp.h>
30#endif
31
32#include "jni.h"
33#include "JNIHelp.h"
34#include "android_runtime/AndroidRuntime.h"
35#include "private/android_filesystem_config.h"
36
37#include "MtpServer.h"
38#include "MtpStorage.h"
39
40using namespace android;
41
42// MtpStorage class
43jclass clazz_MtpStorage;
44
45// MtpStorage fields
46static jfieldID field_MtpStorage_storageId;
47static jfieldID field_MtpStorage_path;
48static jfieldID field_MtpStorage_description;
49static jfieldID field_MtpStorage_reserveSpace;
50static jfieldID field_MtpStorage_removable;
51
52static Mutex sMutex;
53
54// ----------------------------------------------------------------------------
55
56// in android_mtp_MtpDatabase.cpp
57extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
58
59// ----------------------------------------------------------------------------
60
61#ifdef HAVE_ANDROID_OS
62
63static bool ExceptionCheck(void* env)
64{
65    return ((JNIEnv *)env)->ExceptionCheck();
66}
67
68class MtpThread : public Thread {
69private:
70    MtpDatabase*    mDatabase;
71    MtpServer*      mServer;
72    MtpStorageList  mStorageList;
73    int             mFd;
74
75public:
76    MtpThread(MtpDatabase* database)
77        :   mDatabase(database),
78            mServer(NULL),
79            mFd(-1)
80    {
81    }
82
83    virtual ~MtpThread() {
84    }
85
86    void addStorage(MtpStorage *storage) {
87        mStorageList.push(storage);
88        if (mServer)
89            mServer->addStorage(storage);
90    }
91
92    void removeStorage(MtpStorageID id) {
93        MtpStorage* storage = mServer->getStorage(id);
94        if (storage) {
95            for (size_t i = 0; i < mStorageList.size(); i++) {
96                if (mStorageList[i] == storage) {
97                    mStorageList.removeAt(i);
98                    break;
99                }
100            }
101            if (mServer)
102                mServer->removeStorage(storage);
103            delete storage;
104        }
105    }
106
107    void start() {
108        run("MtpThread");
109    }
110
111    virtual bool threadLoop() {
112        sMutex.lock();
113
114        mFd = open("/dev/mtp_usb", O_RDWR);
115        if (mFd >= 0) {
116            mServer = new MtpServer(mFd, mDatabase, AID_MEDIA_RW, 0664, 0775);
117            for (size_t i = 0; i < mStorageList.size(); i++) {
118                mServer->addStorage(mStorageList[i]);
119            }
120        } else {
121            LOGE("could not open MTP driver, errno: %d", errno);
122        }
123
124        sMutex.unlock();
125        mServer->run();
126        sMutex.lock();
127
128        close(mFd);
129        mFd = -1;
130        delete mServer;
131        mServer = NULL;
132
133        sMutex.unlock();
134        // delay a bit before retrying to avoid excessive spin
135        if (!exitPending()) {
136            sleep(1);
137        }
138
139        return true;
140    }
141
142    void sendObjectAdded(MtpObjectHandle handle) {
143        if (mServer)
144            mServer->sendObjectAdded(handle);
145    }
146
147    void sendObjectRemoved(MtpObjectHandle handle) {
148        if (mServer)
149            mServer->sendObjectRemoved(handle);
150    }
151};
152
153// This smart pointer is necessary for preventing MtpThread from exiting too early
154static sp<MtpThread> sThread;
155
156#endif // HAVE_ANDROID_OS
157
158static void
159android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase)
160{
161#ifdef HAVE_ANDROID_OS
162    // create the thread and assign it to the smart pointer
163    sThread = new MtpThread(getMtpDatabase(env, javaDatabase));
164#endif
165}
166
167static void
168android_mtp_MtpServer_start(JNIEnv *env, jobject thiz)
169{
170#ifdef HAVE_ANDROID_OS
171   sMutex.lock();
172    MtpThread *thread = sThread.get();
173    if (thread)
174        thread->start();
175    sMutex.unlock();
176#endif // HAVE_ANDROID_OS
177}
178
179static void
180android_mtp_MtpServer_stop(JNIEnv *env, jobject thiz)
181{
182#ifdef HAVE_ANDROID_OS
183    sMutex.lock();
184    MtpThread *thread = sThread.get();
185    if (thread) {
186        thread->requestExitAndWait();
187        sThread = NULL;
188    }
189    sMutex.unlock();
190#endif
191}
192
193static void
194android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle)
195{
196#ifdef HAVE_ANDROID_OS
197    sMutex.lock();
198    MtpThread *thread = sThread.get();
199    if (thread)
200        thread->sendObjectAdded(handle);
201    sMutex.unlock();
202#endif
203}
204
205static void
206android_mtp_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle)
207{
208#ifdef HAVE_ANDROID_OS
209    sMutex.lock();
210    MtpThread *thread = sThread.get();
211    if (thread)
212        thread->sendObjectRemoved(handle);
213    sMutex.unlock();
214#endif
215}
216
217static void
218android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage)
219{
220#ifdef HAVE_ANDROID_OS
221    sMutex.lock();
222    MtpThread *thread = sThread.get();
223    if (thread) {
224        jint storageID = env->GetIntField(jstorage, field_MtpStorage_storageId);
225        jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path);
226        jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description);
227        jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace);
228        jboolean removable = env->GetBooleanField(jstorage, field_MtpStorage_removable);
229
230        const char *pathStr = env->GetStringUTFChars(path, NULL);
231        if (pathStr != NULL) {
232            const char *descriptionStr = env->GetStringUTFChars(description, NULL);
233            if (descriptionStr != NULL) {
234                MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, reserveSpace, removable);
235                thread->addStorage(storage);
236                env->ReleaseStringUTFChars(path, pathStr);
237                env->ReleaseStringUTFChars(description, descriptionStr);
238            } else {
239                env->ReleaseStringUTFChars(path, pathStr);
240            }
241        }
242    } else {
243        LOGE("MtpThread is null in add_storage");
244    }
245    sMutex.unlock();
246#endif
247}
248
249static void
250android_mtp_MtpServer_remove_storage(JNIEnv *env, jobject thiz, jint storageId)
251{
252#ifdef HAVE_ANDROID_OS
253    sMutex.lock();
254    MtpThread *thread = sThread.get();
255    if (thread)
256        thread->removeStorage(storageId);
257    else
258        LOGE("MtpThread is null in remove_storage");
259    sMutex.unlock();
260#endif
261}
262
263// ----------------------------------------------------------------------------
264
265static JNINativeMethod gMethods[] = {
266    {"native_setup",                "(Landroid/mtp/MtpDatabase;)V",
267                                            (void *)android_mtp_MtpServer_setup},
268    {"native_start",                "()V",  (void *)android_mtp_MtpServer_start},
269    {"native_stop",                 "()V",  (void *)android_mtp_MtpServer_stop},
270    {"native_send_object_added",    "(I)V", (void *)android_mtp_MtpServer_send_object_added},
271    {"native_send_object_removed",  "(I)V", (void *)android_mtp_MtpServer_send_object_removed},
272    {"native_add_storage",          "(Landroid/mtp/MtpStorage;)V",
273                                            (void *)android_mtp_MtpServer_add_storage},
274    {"native_remove_storage",       "(I)V", (void *)android_mtp_MtpServer_remove_storage},
275};
276
277static const char* const kClassPathName = "android/mtp/MtpServer";
278
279int register_android_mtp_MtpServer(JNIEnv *env)
280{
281    jclass clazz;
282
283    clazz = env->FindClass("android/mtp/MtpStorage");
284    if (clazz == NULL) {
285        LOGE("Can't find android/mtp/MtpStorage");
286        return -1;
287    }
288    field_MtpStorage_storageId = env->GetFieldID(clazz, "mStorageId", "I");
289    if (field_MtpStorage_storageId == NULL) {
290        LOGE("Can't find MtpStorage.mStorageId");
291        return -1;
292    }
293    field_MtpStorage_path = env->GetFieldID(clazz, "mPath", "Ljava/lang/String;");
294    if (field_MtpStorage_path == NULL) {
295        LOGE("Can't find MtpStorage.mPath");
296        return -1;
297    }
298    field_MtpStorage_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
299    if (field_MtpStorage_description == NULL) {
300        LOGE("Can't find MtpStorage.mDescription");
301        return -1;
302    }
303    field_MtpStorage_reserveSpace = env->GetFieldID(clazz, "mReserveSpace", "J");
304    if (field_MtpStorage_reserveSpace == NULL) {
305        LOGE("Can't find MtpStorage.mReserveSpace");
306        return -1;
307    }
308    field_MtpStorage_removable = env->GetFieldID(clazz, "mRemovable", "Z");
309    if (field_MtpStorage_removable == NULL) {
310        LOGE("Can't find MtpStorage.mRemovable");
311        return -1;
312    }
313    clazz_MtpStorage = (jclass)env->NewGlobalRef(clazz);
314
315    clazz = env->FindClass("android/mtp/MtpServer");
316    if (clazz == NULL) {
317        LOGE("Can't find android/mtp/MtpServer");
318        return -1;
319    }
320
321    return AndroidRuntime::registerNativeMethods(env,
322                "android/mtp/MtpServer", gMethods, NELEM(gMethods));
323}
324