android_mtp_MtpServer.cpp revision 66e57f6aa9d206552e9b154bf00a17d6efae7fb0
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// ----------------------------------------------------------------------------
43
44// in android_mtp_MtpDatabase.cpp
45extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
46
47// ----------------------------------------------------------------------------
48
49#ifdef HAVE_ANDROID_OS
50
51static bool ExceptionCheck(void* env)
52{
53    return ((JNIEnv *)env)->ExceptionCheck();
54}
55
56class MtpThread : public Thread {
57private:
58    MtpDatabase*    mDatabase;
59    MtpServer*      mServer;
60    MtpStorage*     mStorage;
61    Mutex           mMutex;
62    bool            mUsePtp;
63    bool            mLocked;
64    int             mFd;
65
66public:
67    MtpThread(MtpDatabase* database, MtpStorage* storage)
68        :   mDatabase(database),
69            mServer(NULL),
70            mStorage(storage),
71            mUsePtp(false),
72            mLocked(false),
73            mFd(-1)
74    {
75    }
76
77    virtual ~MtpThread() {
78        delete mStorage;
79    }
80
81    void setPtpMode(bool usePtp) {
82        mMutex.lock();
83        mUsePtp = usePtp;
84        mMutex.unlock();
85    }
86
87    void setLocked(bool locked) {
88        mMutex.lock();
89        if (locked != mLocked) {
90            if (mServer) {
91                if (locked)
92                    mServer->removeStorage(mStorage);
93                else
94                    mServer->addStorage(mStorage);
95            }
96            mLocked = locked;
97        }
98        mMutex.unlock();
99    }
100
101    virtual bool threadLoop() {
102        mMutex.lock();
103        mFd = open("/dev/mtp_usb", O_RDWR);
104        if (mFd >= 0) {
105            ioctl(mFd, MTP_SET_INTERFACE_MODE,
106                    (mUsePtp ? MTP_INTERFACE_MODE_PTP : MTP_INTERFACE_MODE_MTP));
107
108            mServer = new MtpServer(mFd, mDatabase, AID_MEDIA_RW, 0664, 0775);
109            if (!mLocked)
110                mServer->addStorage(mStorage);
111
112            mMutex.unlock();
113            mServer->run();
114            mMutex.lock();
115
116            close(mFd);
117            mFd = -1;
118            delete mServer;
119            mServer = NULL;
120        } else {
121            LOGE("could not open MTP driver, errno: %d", errno);
122        }
123        mMutex.unlock();
124        // delay a bit before retrying to avoid excessive spin
125        if (!exitPending()) {
126            sleep(1);
127        }
128
129        return true;
130    }
131
132    void sendObjectAdded(MtpObjectHandle handle) {
133        mMutex.lock();
134        if (mServer)
135            mServer->sendObjectAdded(handle);
136        mMutex.unlock();
137    }
138
139    void sendObjectRemoved(MtpObjectHandle handle) {
140        mMutex.lock();
141        if (mServer)
142            mServer->sendObjectRemoved(handle);
143        mMutex.unlock();
144    }
145};
146
147// This smart pointer is necessary for preventing MtpThread from exiting too early
148static sp<MtpThread> sThread;
149
150#endif // HAVE_ANDROID_OS
151
152static void
153android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase,
154        jstring storagePath, jlong reserveSpace)
155{
156#ifdef HAVE_ANDROID_OS
157    MtpDatabase* database = getMtpDatabase(env, javaDatabase);
158    const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL);
159
160    // create the thread and assign it to the smart pointer
161    MtpStorage* storage = new MtpStorage(MTP_FIRST_STORAGE_ID, storagePathStr, reserveSpace);
162    sThread = new MtpThread(database, storage);
163
164    env->ReleaseStringUTFChars(storagePath, storagePathStr);
165#endif
166}
167
168static void
169android_mtp_MtpServer_start(JNIEnv *env, jobject thiz)
170{
171#ifdef HAVE_ANDROID_OS
172    MtpThread *thread = sThread.get();
173    if (thread)
174        thread->run("MtpThread");
175#endif // HAVE_ANDROID_OS
176}
177
178static void
179android_mtp_MtpServer_stop(JNIEnv *env, jobject thiz)
180{
181#ifdef HAVE_ANDROID_OS
182    MtpThread *thread = sThread.get();
183    if (thread) {
184        thread->requestExitAndWait();
185        sThread = NULL;
186    }
187#endif
188}
189
190static void
191android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle)
192{
193#ifdef HAVE_ANDROID_OS
194    MtpThread *thread = sThread.get();
195    if (thread)
196        thread->sendObjectAdded(handle);
197#endif
198}
199
200static void
201android_mtp_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle)
202{
203#ifdef HAVE_ANDROID_OS
204    MtpThread *thread = sThread.get();
205    if (thread)
206        thread->sendObjectRemoved(handle);
207#endif
208}
209
210static void
211android_mtp_MtpServer_set_ptp_mode(JNIEnv *env, jobject thiz, jboolean usePtp)
212{
213#ifdef HAVE_ANDROID_OS
214    MtpThread *thread = sThread.get();
215    if (thread)
216        thread->setPtpMode(usePtp);
217#endif
218}
219
220static void
221android_mtp_MtpServer_set_locked(JNIEnv *env, jobject thiz, jboolean locked)
222{
223#ifdef HAVE_ANDROID_OS
224    MtpThread *thread = sThread.get();
225    if (thread)
226        thread->setLocked(locked);
227#endif
228}
229
230// ----------------------------------------------------------------------------
231
232static JNINativeMethod gMethods[] = {
233    {"native_setup",                "(Landroid/mtp/MtpDatabase;Ljava/lang/String;J)V",
234                                            (void *)android_mtp_MtpServer_setup},
235    {"native_start",                "()V",  (void *)android_mtp_MtpServer_start},
236    {"native_stop",                 "()V",  (void *)android_mtp_MtpServer_stop},
237    {"native_send_object_added",    "(I)V", (void *)android_mtp_MtpServer_send_object_added},
238    {"native_send_object_removed",  "(I)V", (void *)android_mtp_MtpServer_send_object_removed},
239    {"native_set_ptp_mode",         "(Z)V", (void *)android_mtp_MtpServer_set_ptp_mode},
240    {"native_set_locked",           "(Z)V", (void *)android_mtp_MtpServer_set_locked},
241};
242
243static const char* const kClassPathName = "android/mtp/MtpServer";
244
245int register_android_mtp_MtpServer(JNIEnv *env)
246{
247    jclass clazz;
248
249    clazz = env->FindClass("android/mtp/MtpServer");
250    if (clazz == NULL) {
251        LOGE("Can't find android/mtp/MtpServer");
252        return -1;
253    }
254
255    return AndroidRuntime::registerNativeMethods(env,
256                "android/mtp/MtpServer", gMethods, NELEM(gMethods));
257}
258