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 <utils/threads.h>
26
27#include "jni.h"
28#include "JNIHelp.h"
29#include "android_runtime/AndroidRuntime.h"
30#include "private/android_filesystem_config.h"
31
32#include "MtpServer.h"
33#include "MtpStorage.h"
34
35using namespace android;
36
37// MtpServer fields
38static jfieldID field_MtpServer_nativeContext;
39
40// MtpStorage fields
41static jfieldID field_MtpStorage_storageId;
42static jfieldID field_MtpStorage_path;
43static jfieldID field_MtpStorage_description;
44static jfieldID field_MtpStorage_reserveSpace;
45static jfieldID field_MtpStorage_removable;
46static jfieldID field_MtpStorage_maxFileSize;
47
48static Mutex sMutex;
49
50// ----------------------------------------------------------------------------
51
52// in android_mtp_MtpDatabase.cpp
53extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
54
55static inline MtpServer* getMtpServer(JNIEnv *env, jobject thiz) {
56    return (MtpServer*)env->GetLongField(thiz, field_MtpServer_nativeContext);
57}
58
59static void
60android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jboolean usePtp)
61{
62    int fd = open("/dev/mtp_usb", O_RDWR);
63    if (fd >= 0) {
64        MtpServer* server = new MtpServer(fd, getMtpDatabase(env, javaDatabase),
65                usePtp, AID_MEDIA_RW, 0664, 0775);
66        env->SetLongField(thiz, field_MtpServer_nativeContext, (jlong)server);
67    } else {
68        ALOGE("could not open MTP driver, errno: %d", errno);
69    }
70}
71
72static void
73android_mtp_MtpServer_run(JNIEnv *env, jobject thiz)
74{
75    MtpServer* server = getMtpServer(env, thiz);
76    if (server)
77        server->run();
78    else
79        ALOGE("server is null in run");
80}
81
82static void
83android_mtp_MtpServer_cleanup(JNIEnv *env, jobject thiz)
84{
85    Mutex::Autolock autoLock(sMutex);
86
87    MtpServer* server = getMtpServer(env, thiz);
88    if (server) {
89        delete server;
90        env->SetLongField(thiz, field_MtpServer_nativeContext, 0);
91    } else {
92        ALOGE("server is null in cleanup");
93    }
94}
95
96static void
97android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle)
98{
99    Mutex::Autolock autoLock(sMutex);
100
101    MtpServer* server = getMtpServer(env, thiz);
102    if (server)
103        server->sendObjectAdded(handle);
104    else
105        ALOGE("server is null in send_object_added");
106}
107
108static void
109android_mtp_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle)
110{
111    Mutex::Autolock autoLock(sMutex);
112
113    MtpServer* server = getMtpServer(env, thiz);
114    if (server)
115        server->sendObjectRemoved(handle);
116    else
117        ALOGE("server is null in send_object_removed");
118}
119
120static void
121android_mtp_MtpServer_send_device_property_changed(JNIEnv *env, jobject thiz, jint property)
122{
123    Mutex::Autolock autoLock(sMutex);
124
125    MtpServer* server = getMtpServer(env, thiz);
126    if (server)
127        server->sendDevicePropertyChanged(property);
128    else
129        ALOGE("server is null in send_object_removed");
130}
131
132static void
133android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage)
134{
135    Mutex::Autolock autoLock(sMutex);
136
137    MtpServer* server = getMtpServer(env, thiz);
138    if (server) {
139        jint storageID = env->GetIntField(jstorage, field_MtpStorage_storageId);
140        jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path);
141        jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description);
142        jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace);
143        jboolean removable = env->GetBooleanField(jstorage, field_MtpStorage_removable);
144        jlong maxFileSize = env->GetLongField(jstorage, field_MtpStorage_maxFileSize);
145
146        const char *pathStr = env->GetStringUTFChars(path, NULL);
147        if (pathStr != NULL) {
148            const char *descriptionStr = env->GetStringUTFChars(description, NULL);
149            if (descriptionStr != NULL) {
150                MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr,
151                        reserveSpace, removable, maxFileSize);
152                server->addStorage(storage);
153                env->ReleaseStringUTFChars(path, pathStr);
154                env->ReleaseStringUTFChars(description, descriptionStr);
155            } else {
156                env->ReleaseStringUTFChars(path, pathStr);
157            }
158        }
159    } else {
160        ALOGE("server is null in add_storage");
161    }
162}
163
164static void
165android_mtp_MtpServer_remove_storage(JNIEnv *env, jobject thiz, jint storageId)
166{
167    Mutex::Autolock autoLock(sMutex);
168
169    MtpServer* server = getMtpServer(env, thiz);
170    if (server) {
171        MtpStorage* storage = server->getStorage(storageId);
172        if (storage) {
173            server->removeStorage(storage);
174            delete storage;
175        }
176    } else
177        ALOGE("server is null in remove_storage");
178}
179
180// ----------------------------------------------------------------------------
181
182static JNINativeMethod gMethods[] = {
183    {"native_setup",                "(Landroid/mtp/MtpDatabase;Z)V",
184                                            (void *)android_mtp_MtpServer_setup},
185    {"native_run",                  "()V",  (void *)android_mtp_MtpServer_run},
186    {"native_cleanup",              "()V",  (void *)android_mtp_MtpServer_cleanup},
187    {"native_send_object_added",    "(I)V", (void *)android_mtp_MtpServer_send_object_added},
188    {"native_send_object_removed",  "(I)V", (void *)android_mtp_MtpServer_send_object_removed},
189    {"native_send_device_property_changed",  "(I)V",
190                                    (void *)android_mtp_MtpServer_send_device_property_changed},
191    {"native_add_storage",          "(Landroid/mtp/MtpStorage;)V",
192                                            (void *)android_mtp_MtpServer_add_storage},
193    {"native_remove_storage",       "(I)V", (void *)android_mtp_MtpServer_remove_storage},
194};
195
196static const char* const kClassPathName = "android/mtp/MtpServer";
197
198int register_android_mtp_MtpServer(JNIEnv *env)
199{
200    jclass clazz;
201
202    clazz = env->FindClass("android/mtp/MtpStorage");
203    if (clazz == NULL) {
204        ALOGE("Can't find android/mtp/MtpStorage");
205        return -1;
206    }
207    field_MtpStorage_storageId = env->GetFieldID(clazz, "mStorageId", "I");
208    if (field_MtpStorage_storageId == NULL) {
209        ALOGE("Can't find MtpStorage.mStorageId");
210        return -1;
211    }
212    field_MtpStorage_path = env->GetFieldID(clazz, "mPath", "Ljava/lang/String;");
213    if (field_MtpStorage_path == NULL) {
214        ALOGE("Can't find MtpStorage.mPath");
215        return -1;
216    }
217    field_MtpStorage_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
218    if (field_MtpStorage_description == NULL) {
219        ALOGE("Can't find MtpStorage.mDescription");
220        return -1;
221    }
222    field_MtpStorage_reserveSpace = env->GetFieldID(clazz, "mReserveSpace", "J");
223    if (field_MtpStorage_reserveSpace == NULL) {
224        ALOGE("Can't find MtpStorage.mReserveSpace");
225        return -1;
226    }
227    field_MtpStorage_removable = env->GetFieldID(clazz, "mRemovable", "Z");
228    if (field_MtpStorage_removable == NULL) {
229        ALOGE("Can't find MtpStorage.mRemovable");
230        return -1;
231    }
232    field_MtpStorage_maxFileSize = env->GetFieldID(clazz, "mMaxFileSize", "J");
233    if (field_MtpStorage_maxFileSize == NULL) {
234        ALOGE("Can't find MtpStorage.mMaxFileSize");
235        return -1;
236    }
237
238    clazz = env->FindClass("android/mtp/MtpServer");
239    if (clazz == NULL) {
240        ALOGE("Can't find android/mtp/MtpServer");
241        return -1;
242    }
243    field_MtpServer_nativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
244    if (field_MtpServer_nativeContext == NULL) {
245        ALOGE("Can't find MtpServer.mNativeContext");
246        return -1;
247    }
248
249    return AndroidRuntime::registerNativeMethods(env,
250                "android/mtp/MtpServer", gMethods, NELEM(gMethods));
251}
252