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 android_mtp_configure(JNIEnv *, jobject, jboolean usePtp) {
60    MtpServer::configure(usePtp);
61}
62
63static void
64android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jboolean usePtp,
65        jstring deviceInfoManufacturer,
66        jstring deviceInfoModel,
67        jstring deviceInfoDeviceVersion,
68        jstring deviceInfoSerialNumber)
69{
70    const char *deviceInfoManufacturerStr = env->GetStringUTFChars(deviceInfoManufacturer, NULL);
71    const char *deviceInfoModelStr = env->GetStringUTFChars(deviceInfoModel, NULL);
72    const char *deviceInfoDeviceVersionStr = env->GetStringUTFChars(deviceInfoDeviceVersion, NULL);
73    const char *deviceInfoSerialNumberStr = env->GetStringUTFChars(deviceInfoSerialNumber, NULL);
74    MtpServer* server = new MtpServer(getMtpDatabase(env, javaDatabase),
75            usePtp, AID_MEDIA_RW, 0664, 0775,
76            MtpString((deviceInfoManufacturerStr != NULL) ? deviceInfoManufacturerStr : ""),
77            MtpString((deviceInfoModelStr != NULL) ? deviceInfoModelStr : ""),
78            MtpString((deviceInfoDeviceVersionStr != NULL) ? deviceInfoDeviceVersionStr : ""),
79            MtpString((deviceInfoSerialNumberStr != NULL) ? deviceInfoSerialNumberStr : ""));
80    if (deviceInfoManufacturerStr != NULL) {
81        env->ReleaseStringUTFChars(deviceInfoManufacturer, deviceInfoManufacturerStr);
82    }
83    if (deviceInfoModelStr != NULL) {
84        env->ReleaseStringUTFChars(deviceInfoModel, deviceInfoModelStr);
85    }
86    if (deviceInfoDeviceVersionStr != NULL) {
87        env->ReleaseStringUTFChars(deviceInfoDeviceVersion, deviceInfoDeviceVersionStr);
88    }
89    if (deviceInfoSerialNumberStr != NULL) {
90        env->ReleaseStringUTFChars(deviceInfoSerialNumber, deviceInfoSerialNumberStr);
91    }
92    env->SetLongField(thiz, field_MtpServer_nativeContext, (jlong)server);
93}
94
95static void
96android_mtp_MtpServer_run(JNIEnv *env, jobject thiz)
97{
98    MtpServer* server = getMtpServer(env, thiz);
99    if (server)
100        server->run();
101    else
102        ALOGE("server is null in run");
103}
104
105static void
106android_mtp_MtpServer_cleanup(JNIEnv *env, jobject thiz)
107{
108    Mutex::Autolock autoLock(sMutex);
109
110    MtpServer* server = getMtpServer(env, thiz);
111    if (server) {
112        delete server;
113        env->SetLongField(thiz, field_MtpServer_nativeContext, 0);
114    } else {
115        ALOGE("server is null in cleanup");
116    }
117}
118
119static void
120android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle)
121{
122    Mutex::Autolock autoLock(sMutex);
123
124    MtpServer* server = getMtpServer(env, thiz);
125    if (server)
126        server->sendObjectAdded(handle);
127    else
128        ALOGE("server is null in send_object_added");
129}
130
131static void
132android_mtp_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle)
133{
134    Mutex::Autolock autoLock(sMutex);
135
136    MtpServer* server = getMtpServer(env, thiz);
137    if (server)
138        server->sendObjectRemoved(handle);
139    else
140        ALOGE("server is null in send_object_removed");
141}
142
143static void
144android_mtp_MtpServer_send_device_property_changed(JNIEnv *env, jobject thiz, jint property)
145{
146    Mutex::Autolock autoLock(sMutex);
147
148    MtpServer* server = getMtpServer(env, thiz);
149    if (server)
150        server->sendDevicePropertyChanged(property);
151    else
152        ALOGE("server is null in send_object_removed");
153}
154
155static void
156android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage)
157{
158    Mutex::Autolock autoLock(sMutex);
159
160    MtpServer* server = getMtpServer(env, thiz);
161    if (server) {
162        jint storageID = env->GetIntField(jstorage, field_MtpStorage_storageId);
163        jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path);
164        jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description);
165        jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace);
166        jboolean removable = env->GetBooleanField(jstorage, field_MtpStorage_removable);
167        jlong maxFileSize = env->GetLongField(jstorage, field_MtpStorage_maxFileSize);
168
169        const char *pathStr = env->GetStringUTFChars(path, NULL);
170        if (pathStr != NULL) {
171            const char *descriptionStr = env->GetStringUTFChars(description, NULL);
172            if (descriptionStr != NULL) {
173                MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr,
174                        reserveSpace, removable, maxFileSize);
175                server->addStorage(storage);
176                env->ReleaseStringUTFChars(path, pathStr);
177                env->ReleaseStringUTFChars(description, descriptionStr);
178            } else {
179                env->ReleaseStringUTFChars(path, pathStr);
180            }
181        }
182    } else {
183        ALOGE("server is null in add_storage");
184    }
185}
186
187static void
188android_mtp_MtpServer_remove_storage(JNIEnv *env, jobject thiz, jint storageId)
189{
190    Mutex::Autolock autoLock(sMutex);
191
192    MtpServer* server = getMtpServer(env, thiz);
193    if (server) {
194        MtpStorage* storage = server->getStorage(storageId);
195        if (storage) {
196            server->removeStorage(storage);
197            delete storage;
198        }
199    } else
200        ALOGE("server is null in remove_storage");
201}
202
203// ----------------------------------------------------------------------------
204
205static const JNINativeMethod gMethods[] = {
206    {"native_configure",              "(Z)V",  (void *)android_mtp_configure},
207    {"native_setup",                "(Landroid/mtp/MtpDatabase;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
208                                            (void *)android_mtp_MtpServer_setup},
209    {"native_run",                  "()V",  (void *)android_mtp_MtpServer_run},
210    {"native_cleanup",              "()V",  (void *)android_mtp_MtpServer_cleanup},
211    {"native_send_object_added",    "(I)V", (void *)android_mtp_MtpServer_send_object_added},
212    {"native_send_object_removed",  "(I)V", (void *)android_mtp_MtpServer_send_object_removed},
213    {"native_send_device_property_changed",  "(I)V",
214                                    (void *)android_mtp_MtpServer_send_device_property_changed},
215    {"native_add_storage",          "(Landroid/mtp/MtpStorage;)V",
216                                            (void *)android_mtp_MtpServer_add_storage},
217    {"native_remove_storage",       "(I)V", (void *)android_mtp_MtpServer_remove_storage},
218};
219
220int register_android_mtp_MtpServer(JNIEnv *env)
221{
222    jclass clazz;
223
224    clazz = env->FindClass("android/mtp/MtpStorage");
225    if (clazz == NULL) {
226        ALOGE("Can't find android/mtp/MtpStorage");
227        return -1;
228    }
229    field_MtpStorage_storageId = env->GetFieldID(clazz, "mStorageId", "I");
230    if (field_MtpStorage_storageId == NULL) {
231        ALOGE("Can't find MtpStorage.mStorageId");
232        return -1;
233    }
234    field_MtpStorage_path = env->GetFieldID(clazz, "mPath", "Ljava/lang/String;");
235    if (field_MtpStorage_path == NULL) {
236        ALOGE("Can't find MtpStorage.mPath");
237        return -1;
238    }
239    field_MtpStorage_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
240    if (field_MtpStorage_description == NULL) {
241        ALOGE("Can't find MtpStorage.mDescription");
242        return -1;
243    }
244    field_MtpStorage_reserveSpace = env->GetFieldID(clazz, "mReserveSpace", "J");
245    if (field_MtpStorage_reserveSpace == NULL) {
246        ALOGE("Can't find MtpStorage.mReserveSpace");
247        return -1;
248    }
249    field_MtpStorage_removable = env->GetFieldID(clazz, "mRemovable", "Z");
250    if (field_MtpStorage_removable == NULL) {
251        ALOGE("Can't find MtpStorage.mRemovable");
252        return -1;
253    }
254    field_MtpStorage_maxFileSize = env->GetFieldID(clazz, "mMaxFileSize", "J");
255    if (field_MtpStorage_maxFileSize == NULL) {
256        ALOGE("Can't find MtpStorage.mMaxFileSize");
257        return -1;
258    }
259
260    clazz = env->FindClass("android/mtp/MtpServer");
261    if (clazz == NULL) {
262        ALOGE("Can't find android/mtp/MtpServer");
263        return -1;
264    }
265    field_MtpServer_nativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
266    if (field_MtpServer_nativeContext == NULL) {
267        ALOGE("Can't find MtpServer.mNativeContext");
268        return -1;
269    }
270
271    return AndroidRuntime::registerNativeMethods(env,
272                "android/mtp/MtpServer", gMethods, NELEM(gMethods));
273}
274