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 
35 using namespace android;
36 
37 // MtpServer fields
38 static jfieldID field_MtpServer_nativeContext;
39 
40 // MtpStorage fields
41 static jfieldID field_MtpStorage_storageId;
42 static jfieldID field_MtpStorage_path;
43 static jfieldID field_MtpStorage_description;
44 static jfieldID field_MtpStorage_reserveSpace;
45 static jfieldID field_MtpStorage_removable;
46 static jfieldID field_MtpStorage_maxFileSize;
47 
48 static Mutex sMutex;
49 
50 // ----------------------------------------------------------------------------
51 
52 // in android_mtp_MtpDatabase.cpp
53 extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
54 
getMtpServer(JNIEnv * env,jobject thiz)55 static inline MtpServer* getMtpServer(JNIEnv *env, jobject thiz) {
56     return (MtpServer*)env->GetLongField(thiz, field_MtpServer_nativeContext);
57 }
58 
59 static void
android_mtp_MtpServer_setup(JNIEnv * env,jobject thiz,jobject javaDatabase,jboolean usePtp)60 android_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 
72 static void
android_mtp_MtpServer_run(JNIEnv * env,jobject thiz)73 android_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 
82 static void
android_mtp_MtpServer_cleanup(JNIEnv * env,jobject thiz)83 android_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 
96 static void
android_mtp_MtpServer_send_object_added(JNIEnv * env,jobject thiz,jint handle)97 android_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 
108 static void
android_mtp_MtpServer_send_object_removed(JNIEnv * env,jobject thiz,jint handle)109 android_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 
120 static void
android_mtp_MtpServer_send_device_property_changed(JNIEnv * env,jobject thiz,jint property)121 android_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 
132 static void
android_mtp_MtpServer_add_storage(JNIEnv * env,jobject thiz,jobject jstorage)133 android_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 
164 static void
android_mtp_MtpServer_remove_storage(JNIEnv * env,jobject thiz,jint storageId)165 android_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 
182 static 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 
register_android_mtp_MtpServer(JNIEnv * env)196 int register_android_mtp_MtpServer(JNIEnv *env)
197 {
198     jclass clazz;
199 
200     clazz = env->FindClass("android/mtp/MtpStorage");
201     if (clazz == NULL) {
202         ALOGE("Can't find android/mtp/MtpStorage");
203         return -1;
204     }
205     field_MtpStorage_storageId = env->GetFieldID(clazz, "mStorageId", "I");
206     if (field_MtpStorage_storageId == NULL) {
207         ALOGE("Can't find MtpStorage.mStorageId");
208         return -1;
209     }
210     field_MtpStorage_path = env->GetFieldID(clazz, "mPath", "Ljava/lang/String;");
211     if (field_MtpStorage_path == NULL) {
212         ALOGE("Can't find MtpStorage.mPath");
213         return -1;
214     }
215     field_MtpStorage_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
216     if (field_MtpStorage_description == NULL) {
217         ALOGE("Can't find MtpStorage.mDescription");
218         return -1;
219     }
220     field_MtpStorage_reserveSpace = env->GetFieldID(clazz, "mReserveSpace", "J");
221     if (field_MtpStorage_reserveSpace == NULL) {
222         ALOGE("Can't find MtpStorage.mReserveSpace");
223         return -1;
224     }
225     field_MtpStorage_removable = env->GetFieldID(clazz, "mRemovable", "Z");
226     if (field_MtpStorage_removable == NULL) {
227         ALOGE("Can't find MtpStorage.mRemovable");
228         return -1;
229     }
230     field_MtpStorage_maxFileSize = env->GetFieldID(clazz, "mMaxFileSize", "J");
231     if (field_MtpStorage_maxFileSize == NULL) {
232         ALOGE("Can't find MtpStorage.mMaxFileSize");
233         return -1;
234     }
235 
236     clazz = env->FindClass("android/mtp/MtpServer");
237     if (clazz == NULL) {
238         ALOGE("Can't find android/mtp/MtpServer");
239         return -1;
240     }
241     field_MtpServer_nativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
242     if (field_MtpServer_nativeContext == NULL) {
243         ALOGE("Can't find MtpServer.mNativeContext");
244         return -1;
245     }
246 
247     return AndroidRuntime::registerNativeMethods(env,
248                 "android/mtp/MtpServer", gMethods, NELEM(gMethods));
249 }
250