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