android_mtp_MtpServer.cpp revision 897f894e88a3a1c9010309f04c6bf466125ff818
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
39using namespace android;
40
41// ----------------------------------------------------------------------------
42
43static jfieldID field_context;
44static Mutex    sMutex;
45
46// in android_mtp_MtpDatabase.cpp
47extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
48
49// ----------------------------------------------------------------------------
50
51#ifdef HAVE_ANDROID_OS
52
53static bool ExceptionCheck(void* env)
54{
55    return ((JNIEnv *)env)->ExceptionCheck();
56}
57
58class MtpThread : public Thread {
59private:
60    MtpDatabase*    mDatabase;
61    MtpServer*      mServer;
62    String8         mStoragePath;
63    uint64_t        mReserveSpace;
64    jobject         mJavaServer;
65    bool            mDone;
66    int             mFd;
67
68public:
69    MtpThread(MtpDatabase* database, const char* storagePath, uint64_t reserveSpace,
70                jobject javaServer)
71        :   mDatabase(database),
72            mServer(NULL),
73            mStoragePath(storagePath),
74            mReserveSpace(reserveSpace),
75            mJavaServer(javaServer),
76            mDone(false),
77            mFd(-1)
78    {
79    }
80
81    void setPtpMode(bool usePtp) {
82        sMutex.lock();
83        if (mFd >= 0) {
84            ioctl(mFd, MTP_SET_INTERFACE_MODE,
85                    (usePtp ? MTP_INTERFACE_MODE_PTP : MTP_INTERFACE_MODE_MTP));
86        } else {
87            int fd = open("/dev/mtp_usb", O_RDWR);
88            if (fd >= 0) {
89                ioctl(fd, MTP_SET_INTERFACE_MODE,
90                        (usePtp ? MTP_INTERFACE_MODE_PTP : MTP_INTERFACE_MODE_MTP));
91                close(fd);
92            }
93        }
94        sMutex.unlock();
95    }
96
97    virtual bool threadLoop() {
98        sMutex.lock();
99
100        while (!mDone) {
101            mFd = open("/dev/mtp_usb", O_RDWR);
102            printf("open returned %d\n", mFd);
103            if (mFd < 0) {
104                LOGE("could not open MTP driver\n");
105                sMutex.unlock();
106                return false;
107            }
108
109            mServer = new MtpServer(mFd, mDatabase, AID_MEDIA_RW, 0664, 0775);
110            mServer->addStorage(mStoragePath, mReserveSpace);
111
112            sMutex.unlock();
113
114            LOGD("MtpThread mServer->run");
115            mServer->run();
116            sleep(1);
117
118            sMutex.lock();
119
120            close(mFd);
121            mFd = -1;
122            delete mServer;
123            mServer = NULL;
124        }
125
126        JNIEnv* env = AndroidRuntime::getJNIEnv();
127        env->SetIntField(mJavaServer, field_context, 0);
128        env->DeleteGlobalRef(mJavaServer);
129        sMutex.unlock();
130
131        LOGD("threadLoop returning");
132        return false;
133    }
134
135    void stop() {
136        sMutex.lock();
137        mDone = true;
138        sMutex.unlock();
139    }
140
141    void sendObjectAdded(MtpObjectHandle handle) {
142        sMutex.lock();
143        if (mServer)
144            mServer->sendObjectAdded(handle);
145        sMutex.unlock();
146    }
147
148    void sendObjectRemoved(MtpObjectHandle handle) {
149        sMutex.lock();
150        if (mServer)
151            mServer->sendObjectRemoved(handle);
152        sMutex.unlock();
153    }
154};
155
156#endif // HAVE_ANDROID_OS
157
158static void
159android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase,
160        jstring storagePath, jlong reserveSpace)
161{
162#ifdef HAVE_ANDROID_OS
163    LOGD("setup\n");
164
165    MtpDatabase* database = getMtpDatabase(env, javaDatabase);
166    const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL);
167
168    MtpThread* thread = new MtpThread(database, storagePathStr,
169            reserveSpace, env->NewGlobalRef(thiz));
170    env->SetIntField(thiz, field_context, (int)thread);
171
172    env->ReleaseStringUTFChars(storagePath, storagePathStr);
173#endif
174}
175
176static void
177android_mtp_MtpServer_finalize(JNIEnv *env, jobject thiz)
178{
179    LOGD("finalize\n");
180}
181
182
183static void
184android_mtp_MtpServer_start(JNIEnv *env, jobject thiz)
185{
186#ifdef HAVE_ANDROID_OS
187    LOGD("start\n");
188    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
189    thread->run("MtpThread");
190#endif // HAVE_ANDROID_OS
191}
192
193static void
194android_mtp_MtpServer_stop(JNIEnv *env, jobject thiz)
195{
196#ifdef HAVE_ANDROID_OS
197    LOGD("stop\n");
198    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
199    if (thread)
200        thread->stop();
201#endif
202}
203
204static void
205android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle)
206{
207#ifdef HAVE_ANDROID_OS
208    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
209    if (thread)
210        thread->sendObjectAdded(handle);
211#endif
212}
213
214static void
215android_mtp_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle)
216{
217#ifdef HAVE_ANDROID_OS
218    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
219    if (thread)
220        thread->sendObjectRemoved(handle);
221#endif
222}
223
224static void
225android_mtp_MtpServer_set_ptp_mode(JNIEnv *env, jobject thiz, jboolean usePtp)
226{
227#ifdef HAVE_ANDROID_OS
228    LOGD("set_ptp_mode\n");
229    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
230    if (thread)
231        thread->setPtpMode(usePtp);
232#endif
233}
234
235// ----------------------------------------------------------------------------
236
237static JNINativeMethod gMethods[] = {
238    {"native_setup",                "(Landroid/mtp/MtpDatabase;Ljava/lang/String;J)V",
239                                            (void *)android_mtp_MtpServer_setup},
240    {"native_finalize",             "()V",  (void *)android_mtp_MtpServer_finalize},
241    {"native_start",                "()V",  (void *)android_mtp_MtpServer_start},
242    {"native_stop",                 "()V",  (void *)android_mtp_MtpServer_stop},
243    {"native_send_object_added",    "(I)V", (void *)android_mtp_MtpServer_send_object_added},
244    {"native_send_object_removed",  "(I)V", (void *)android_mtp_MtpServer_send_object_removed},
245    {"native_set_ptp_mode",         "(Z)V", (void *)android_mtp_MtpServer_set_ptp_mode},
246};
247
248static const char* const kClassPathName = "android/mtp/MtpServer";
249
250int register_android_mtp_MtpServer(JNIEnv *env)
251{
252    jclass clazz;
253
254    LOGD("register_android_mtp_MtpServer\n");
255
256    clazz = env->FindClass("android/mtp/MtpServer");
257    if (clazz == NULL) {
258        LOGE("Can't find android/mtp/MtpServer");
259        return -1;
260    }
261    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
262    if (field_context == NULL) {
263        LOGE("Can't find MtpServer.mNativeContext");
264        return -1;
265    }
266
267    return AndroidRuntime::registerNativeMethods(env,
268                "android/mtp/MtpServer", gMethods, NELEM(gMethods));
269}
270