blob: 9e1ede55d3d897dd575748e602b4fea12288fe9e [file] [log] [blame]
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001/*
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 "MtpDatabaseJNI"
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
26#include "jni.h"
27#include "JNIHelp.h"
28#include "android_runtime/AndroidRuntime.h"
29
30#include "MtpDatabase.h"
31#include "MtpDataPacket.h"
32#include "MtpUtils.h"
33#include "mtp.h"
34
35using namespace android;
36
37// ----------------------------------------------------------------------------
38
Mike Lockwoodd815f792010-07-12 08:49:01 -040039static jmethodID method_beginSendObject;
40static jmethodID method_endSendObject;
Mike Lockwoodd21eac92010-07-03 00:44:05 -040041static jmethodID method_getObjectList;
42static jmethodID method_getObjectProperty;
43static jmethodID method_getObjectInfo;
44static jmethodID method_getObjectFilePath;
45static jmethodID method_deleteFile;
46static jfieldID field_context;
47
48MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
49 return (MtpDatabase *)env->GetIntField(database, field_context);
50}
51
Mike Lockwoodff164a72010-07-15 15:01:17 -040052#ifdef HAVE_ANDROID_OS
Mike Lockwoodd21eac92010-07-03 00:44:05 -040053// ----------------------------------------------------------------------------
54
55class MyMtpDatabase : public MtpDatabase {
56private:
57 jobject mDatabase;
58 jintArray mIntBuffer;
59 jlongArray mLongBuffer;
60 jcharArray mStringBuffer;
61
62public:
63 MyMtpDatabase(JNIEnv *env, jobject client);
64 virtual ~MyMtpDatabase();
65 void cleanup(JNIEnv *env);
66
Mike Lockwoodd815f792010-07-12 08:49:01 -040067 virtual MtpObjectHandle beginSendObject(const char* path,
Mike Lockwoodd21eac92010-07-03 00:44:05 -040068 MtpObjectFormat format,
69 MtpObjectHandle parent,
70 MtpStorageID storage,
71 uint64_t size,
72 time_t modified);
73
Mike Lockwoodd815f792010-07-12 08:49:01 -040074 virtual void endSendObject(const char* path,
75 MtpObjectHandle handle,
76 MtpObjectFormat format,
77 bool succeeded);
78
Mike Lockwoodd21eac92010-07-03 00:44:05 -040079 virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID,
80 MtpObjectFormat format,
81 MtpObjectHandle parent);
82
83 virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle,
84 MtpObjectProperty property,
85 MtpDataPacket& packet);
86
87 virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle,
88 MtpDataPacket& packet);
89
Mike Lockwood59c777a2010-08-02 10:37:41 -040090 virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle,
Mike Lockwoodd21eac92010-07-03 00:44:05 -040091 MtpString& filePath,
92 int64_t& fileLength);
Mike Lockwood59c777a2010-08-02 10:37:41 -040093 virtual MtpResponseCode deleteFile(MtpObjectHandle handle);
Mike Lockwoodd21eac92010-07-03 00:44:05 -040094
95 bool getPropertyInfo(MtpObjectProperty property, int& type);
96};
97
98MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
99 : mDatabase(env->NewGlobalRef(client)),
100 mIntBuffer(NULL),
101 mLongBuffer(NULL),
102 mStringBuffer(NULL)
103{
104 jintArray intArray;
105 jlongArray longArray;
106 jcharArray charArray;
107
108 // create buffers for out arguments
109 // we don't need to be thread-safe so this is OK
110 intArray = env->NewIntArray(3);
111 if (!intArray)
112 goto out_of_memory;
113 mIntBuffer = (jintArray)env->NewGlobalRef(intArray);
114 longArray = env->NewLongArray(2);
115 if (!longArray)
116 goto out_of_memory;
117 mLongBuffer = (jlongArray)env->NewGlobalRef(longArray);
118 charArray = env->NewCharArray(256);
119 if (!charArray)
120 goto out_of_memory;
121 mStringBuffer = (jcharArray)env->NewGlobalRef(charArray);
122 return;
123
124out_of_memory:
125 env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), NULL);
126}
127
128void MyMtpDatabase::cleanup(JNIEnv *env) {
129 env->DeleteGlobalRef(mDatabase);
130 env->DeleteGlobalRef(mIntBuffer);
131 env->DeleteGlobalRef(mLongBuffer);
132 env->DeleteGlobalRef(mStringBuffer);
133}
134
135MyMtpDatabase::~MyMtpDatabase() {
136}
137
Mike Lockwoodd815f792010-07-12 08:49:01 -0400138MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path,
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400139 MtpObjectFormat format,
140 MtpObjectHandle parent,
141 MtpStorageID storage,
142 uint64_t size,
143 time_t modified) {
144 JNIEnv* env = AndroidRuntime::getJNIEnv();
Mike Lockwoodd815f792010-07-12 08:49:01 -0400145 return env->CallIntMethod(mDatabase, method_beginSendObject, env->NewStringUTF(path),
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400146 (jint)format, (jint)parent, (jint)storage, (jlong)size, (jlong)modified);
147}
148
Mike Lockwoodd815f792010-07-12 08:49:01 -0400149void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle,
150 MtpObjectFormat format, bool succeeded) {
151 JNIEnv* env = AndroidRuntime::getJNIEnv();
152 env->CallVoidMethod(mDatabase, method_endSendObject, env->NewStringUTF(path),
153 (jint)handle, (jint)format, (jboolean)succeeded);
154}
155
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400156MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
157 MtpObjectFormat format,
158 MtpObjectHandle parent) {
159 JNIEnv* env = AndroidRuntime::getJNIEnv();
160 jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectList,
161 (jint)storageID, (jint)format, (jint)parent);
162 if (!array)
163 return NULL;
164 MtpObjectHandleList* list = new MtpObjectHandleList();
165 jint* handles = env->GetIntArrayElements(array, 0);
166 jsize length = env->GetArrayLength(array);
167LOGD("getObjectList length: %d", length);
168 for (int i = 0; i < length; i++) {
169LOGD("push: %d", handles[i]);
170 list->push(handles[i]);
171 }
172 env->ReleaseIntArrayElements(array, handles, 0);
173 return list;
174}
175
176MtpResponseCode MyMtpDatabase::getObjectProperty(MtpObjectHandle handle,
177 MtpObjectProperty property,
178 MtpDataPacket& packet) {
179 int type;
180
181 if (!getPropertyInfo(property, type))
182 return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
183
184 JNIEnv* env = AndroidRuntime::getJNIEnv();
185 jint result = env->CallIntMethod(mDatabase, method_getObjectProperty,
186 (jint)handle, (jint)property, mLongBuffer, mStringBuffer);
187 if (result != MTP_RESPONSE_OK)
188 return result;
189
190 jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
191 jlong longValue = longValues[0];
192 env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
193
194 switch (type) {
195 case MTP_TYPE_INT8:
196 packet.putInt8(longValue);
197 break;
198 case MTP_TYPE_UINT8:
199 packet.putUInt8(longValue);
200 break;
201 case MTP_TYPE_INT16:
202 packet.putInt16(longValue);
203 break;
204 case MTP_TYPE_UINT16:
205 packet.putUInt16(longValue);
206 break;
207 case MTP_TYPE_INT32:
208 packet.putInt32(longValue);
209 break;
210 case MTP_TYPE_UINT32:
211 packet.putUInt32(longValue);
212 break;
213 case MTP_TYPE_INT64:
214 packet.putInt64(longValue);
215 break;
216 case MTP_TYPE_UINT64:
217 packet.putUInt64(longValue);
218 break;
219 case MTP_TYPE_STR:
220 {
221 jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
222 packet.putString(str);
223 env->ReleaseCharArrayElements(mStringBuffer, str, 0);
224 break;
225 }
226 default:
227 LOGE("unsupported object type\n");
228 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
229 }
230 return MTP_RESPONSE_OK;
231}
232
233MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
234 MtpDataPacket& packet) {
235 char date[20];
236
237 JNIEnv* env = AndroidRuntime::getJNIEnv();
238 jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectInfo,
239 (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer);
240 if (!result)
241 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
242
243 jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
244 MtpStorageID storageID = intValues[0];
245 MtpObjectFormat format = intValues[1];
246 MtpObjectHandle parent = intValues[2];
247 env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
248
249 jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
250 uint64_t size = longValues[0];
251 uint64_t modified = longValues[1];
252 env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
253
254 int associationType = (format == MTP_FORMAT_ASSOCIATION ?
255 MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
256 MTP_ASSOCIATION_TYPE_UNDEFINED);
257
258 packet.putUInt32(storageID);
259 packet.putUInt16(format);
260 packet.putUInt16(0); // protection status
261 packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size));
262 packet.putUInt16(0); // thumb format
263 packet.putUInt32(0); // thumb compressed size
264 packet.putUInt32(0); // thumb pix width
265 packet.putUInt32(0); // thumb pix height
266 packet.putUInt32(0); // image pix width
267 packet.putUInt32(0); // image pix height
268 packet.putUInt32(0); // image bit depth
269 packet.putUInt32(parent);
270 packet.putUInt16(associationType);
271 packet.putUInt32(0); // association desc
272 packet.putUInt32(0); // sequence number
273
274 jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
275 packet.putString(str); // file name
276 env->ReleaseCharArrayElements(mStringBuffer, str, 0);
277
278 packet.putEmptyString();
279 formatDateTime(modified, date, sizeof(date));
280 packet.putString(date); // date modified
281 packet.putEmptyString(); // keywords
282
283 return MTP_RESPONSE_OK;
284}
285
Mike Lockwood59c777a2010-08-02 10:37:41 -0400286MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle,
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400287 MtpString& filePath,
288 int64_t& fileLength) {
289 JNIEnv* env = AndroidRuntime::getJNIEnv();
Mike Lockwood59c777a2010-08-02 10:37:41 -0400290 jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400291 (jint)handle, mStringBuffer, mLongBuffer);
Mike Lockwood59c777a2010-08-02 10:37:41 -0400292 if (result != MTP_RESPONSE_OK)
293 return result;
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400294
295 jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
296 filePath.setTo(str, strlen16(str));
297 env->ReleaseCharArrayElements(mStringBuffer, str, 0);
298
299 jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
300 fileLength = longValues[0];
301 env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
302
Mike Lockwood59c777a2010-08-02 10:37:41 -0400303 return result;
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400304}
305
Mike Lockwood59c777a2010-08-02 10:37:41 -0400306MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400307 JNIEnv* env = AndroidRuntime::getJNIEnv();
Mike Lockwood59c777a2010-08-02 10:37:41 -0400308 return env->CallIntMethod(mDatabase, method_deleteFile, (jint)handle);
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400309}
310
311struct PropertyTableEntry {
312 MtpObjectProperty property;
313 int type;
314};
315
316static const PropertyTableEntry kPropertyTable[] = {
317 { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 },
318 { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 },
319 { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32 },
320 { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR },
321 { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 },
322 { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR },
323};
324
325bool MyMtpDatabase::getPropertyInfo(MtpObjectProperty property, int& type) {
326 int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]);
327 const PropertyTableEntry* entry = kPropertyTable;
328 for (int i = 0; i < count; i++, entry++) {
329 if (entry->property == property) {
330 type = entry->type;
331 return true;
332 }
333 }
334 return false;
335}
336
337// ----------------------------------------------------------------------------
338
339static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
340 if (env->ExceptionCheck()) {
341 LOGE("An exception was thrown by callback '%s'.", methodName);
342 LOGE_EX(env);
343 env->ExceptionClear();
344 }
345}
346
Mike Lockwoodff164a72010-07-15 15:01:17 -0400347#endif // HAVE_ANDROID_OS
348
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400349// ----------------------------------------------------------------------------
350
351static void
352android_media_MtpDatabase_setup(JNIEnv *env, jobject thiz)
353{
Mike Lockwoodff164a72010-07-15 15:01:17 -0400354#ifdef HAVE_ANDROID_OS
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400355 LOGD("setup\n");
356 MyMtpDatabase* database = new MyMtpDatabase(env, thiz);
357 env->SetIntField(thiz, field_context, (int)database);
358 checkAndClearExceptionFromCallback(env, __FUNCTION__);
Mike Lockwoodff164a72010-07-15 15:01:17 -0400359#endif
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400360}
361
362static void
363android_media_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
364{
Mike Lockwoodff164a72010-07-15 15:01:17 -0400365#ifdef HAVE_ANDROID_OS
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400366 LOGD("finalize\n");
367 MyMtpDatabase* database = (MyMtpDatabase *)env->GetIntField(thiz, field_context);
368 database->cleanup(env);
369 delete database;
370 env->SetIntField(thiz, field_context, 0);
371 checkAndClearExceptionFromCallback(env, __FUNCTION__);
Mike Lockwoodff164a72010-07-15 15:01:17 -0400372#endif
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400373}
374
375// ----------------------------------------------------------------------------
376
377static JNINativeMethod gMethods[] = {
378 {"native_setup", "()V", (void *)android_media_MtpDatabase_setup},
379 {"native_finalize", "()V", (void *)android_media_MtpDatabase_finalize},
380};
381
382static const char* const kClassPathName = "android/media/MtpDatabase";
383
384int register_android_media_MtpDatabase(JNIEnv *env)
385{
386 jclass clazz;
387
388 LOGD("register_android_media_MtpDatabase\n");
389
390 clazz = env->FindClass("android/media/MtpDatabase");
391 if (clazz == NULL) {
392 LOGE("Can't find android/media/MtpDatabase");
393 return -1;
394 }
Mike Lockwoodd815f792010-07-12 08:49:01 -0400395 method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I");
396 if (method_beginSendObject == NULL) {
397 LOGE("Can't find beginSendObject");
398 return -1;
399 }
400 method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIZ)V");
401 if (method_endSendObject == NULL) {
402 LOGE("Can't find endSendObject");
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400403 return -1;
404 }
405 method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I");
406 if (method_getObjectList == NULL) {
407 LOGE("Can't find getObjectList");
408 return -1;
409 }
410 method_getObjectProperty = env->GetMethodID(clazz, "getObjectProperty", "(II[J[C)I");
411 if (method_getObjectProperty == NULL) {
412 LOGE("Can't find getObjectProperty");
413 return -1;
414 }
415 method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z");
416 if (method_getObjectInfo == NULL) {
417 LOGE("Can't find getObjectInfo");
418 return -1;
419 }
Mike Lockwood59c777a2010-08-02 10:37:41 -0400420 method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)I");
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400421 if (method_getObjectFilePath == NULL) {
422 LOGE("Can't find getObjectFilePath");
423 return -1;
424 }
Mike Lockwood59c777a2010-08-02 10:37:41 -0400425 method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)I");
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400426 if (method_deleteFile == NULL) {
427 LOGE("Can't find deleteFile");
428 return -1;
429 }
430 field_context = env->GetFieldID(clazz, "mNativeContext", "I");
431 if (field_context == NULL) {
432 LOGE("Can't find MtpDatabase.mNativeContext");
433 return -1;
434 }
435
436 return AndroidRuntime::registerNativeMethods(env,
437 "android/media/MtpDatabase", gMethods, NELEM(gMethods));
438}