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