android_mtp_MtpServer.cpp revision f26a586c86b097f975e26fe526ead564ad011bd0
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            mServer->run();
115            sleep(1);
116
117            sMutex.lock();
118
119            close(mFd);
120            mFd = -1;
121            delete mServer;
122            mServer = NULL;
123        }
124
125        JNIEnv* env = AndroidRuntime::getJNIEnv();
126        env->SetIntField(mJavaServer, field_context, 0);
127        env->DeleteGlobalRef(mJavaServer);
128        sMutex.unlock();
129
130        return false;
131    }
132
133    void stop() {
134        sMutex.lock();
135        mDone = true;
136        sMutex.unlock();
137    }
138
139    void sendObjectAdded(MtpObjectHandle handle) {
140        sMutex.lock();
141        if (mServer)
142            mServer->sendObjectAdded(handle);
143        sMutex.unlock();
144    }
145
146    void sendObjectRemoved(MtpObjectHandle handle) {
147        sMutex.lock();
148        if (mServer)
149            mServer->sendObjectRemoved(handle);
150        sMutex.unlock();
151    }
152};
153
154#endif // HAVE_ANDROID_OS
155
156static void
157android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase,
158        jstring storagePath, jlong reserveSpace)
159{
160#ifdef HAVE_ANDROID_OS
161    MtpDatabase* database = getMtpDatabase(env, javaDatabase);
162    const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL);
163
164    MtpThread* thread = new MtpThread(database, storagePathStr,
165            reserveSpace, env->NewGlobalRef(thiz));
166    env->SetIntField(thiz, field_context, (int)thread);
167
168    env->ReleaseStringUTFChars(storagePath, storagePathStr);
169#endif
170}
171
172static void
173android_mtp_MtpServer_start(JNIEnv *env, jobject thiz)
174{
175#ifdef HAVE_ANDROID_OS
176    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
177    thread->run("MtpThread");
178#endif // HAVE_ANDROID_OS
179}
180
181static void
182android_mtp_MtpServer_stop(JNIEnv *env, jobject thiz)
183{
184#ifdef HAVE_ANDROID_OS
185    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
186    if (thread)
187        thread->stop();
188#endif
189}
190
191static void
192android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle)
193{
194#ifdef HAVE_ANDROID_OS
195    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
196    if (thread)
197        thread->sendObjectAdded(handle);
198#endif
199}
200
201static void
202android_mtp_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle)
203{
204#ifdef HAVE_ANDROID_OS
205    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
206    if (thread)
207        thread->sendObjectRemoved(handle);
208#endif
209}
210
211static void
212android_mtp_MtpServer_set_ptp_mode(JNIEnv *env, jobject thiz, jboolean usePtp)
213{
214#ifdef HAVE_ANDROID_OS
215    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
216    if (thread)
217        thread->setPtpMode(usePtp);
218#endif
219}
220
221// ----------------------------------------------------------------------------
222
223static JNINativeMethod gMethods[] = {
224    {"native_setup",                "(Landroid/mtp/MtpDatabase;Ljava/lang/String;J)V",
225                                            (void *)android_mtp_MtpServer_setup},
226    {"native_start",                "()V",  (void *)android_mtp_MtpServer_start},
227    {"native_stop",                 "()V",  (void *)android_mtp_MtpServer_stop},
228    {"native_send_object_added",    "(I)V", (void *)android_mtp_MtpServer_send_object_added},
229    {"native_send_object_removed",  "(I)V", (void *)android_mtp_MtpServer_send_object_removed},
230    {"native_set_ptp_mode",         "(Z)V", (void *)android_mtp_MtpServer_set_ptp_mode},
231};
232
233static const char* const kClassPathName = "android/mtp/MtpServer";
234
235int register_android_mtp_MtpServer(JNIEnv *env)
236{
237    jclass clazz;
238
239    clazz = env->FindClass("android/mtp/MtpServer");
240    if (clazz == NULL) {
241        LOGE("Can't find android/mtp/MtpServer");
242        return -1;
243    }
244    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
245    if (field_context == NULL) {
246        LOGE("Can't find MtpServer.mNativeContext");
247        return -1;
248    }
249
250    return AndroidRuntime::registerNativeMethods(env,
251                "android/mtp/MtpServer", gMethods, NELEM(gMethods));
252}
253