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