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