android_mtp_MtpServer.cpp revision 467ca0de6d7fd55787a37de9dfd7e5325e1c3c6f
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// ---------------------------------------------------------------------------- 43 44// in android_mtp_MtpDatabase.cpp 45extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database); 46 47// ---------------------------------------------------------------------------- 48 49#ifdef HAVE_ANDROID_OS 50 51static bool ExceptionCheck(void* env) 52{ 53 return ((JNIEnv *)env)->ExceptionCheck(); 54} 55 56class MtpThread : public Thread { 57private: 58 MtpDatabase* mDatabase; 59 MtpServer* mServer; 60 MtpStorage* mStorage; 61 Mutex mMutex; 62 bool mUsePtp; 63 int mFd; 64 65public: 66 MtpThread(MtpDatabase* database, MtpStorage* storage) 67 : mDatabase(database), 68 mServer(NULL), 69 mStorage(storage), 70 mFd(-1) 71 { 72 } 73 74 virtual ~MtpThread() { 75 delete mStorage; 76 } 77 78 void setPtpMode(bool usePtp) { 79 mMutex.lock(); 80 mUsePtp = usePtp; 81 mMutex.unlock(); 82 } 83 84 virtual bool threadLoop() { 85 mMutex.lock(); 86 mFd = open("/dev/mtp_usb", O_RDWR); 87 if (mFd >= 0) { 88 ioctl(mFd, MTP_SET_INTERFACE_MODE, 89 (mUsePtp ? MTP_INTERFACE_MODE_PTP : MTP_INTERFACE_MODE_MTP)); 90 91 mServer = new MtpServer(mFd, mDatabase, AID_MEDIA_RW, 0664, 0775); 92 mServer->addStorage(mStorage); 93 94 mMutex.unlock(); 95 mServer->run(); 96 mMutex.lock(); 97 98 close(mFd); 99 mFd = -1; 100 delete mServer; 101 mServer = NULL; 102 } else { 103 LOGE("could not open MTP driver, errno: %d", errno); 104 } 105 mMutex.unlock(); 106 // delay a bit before retrying to avoid excessive spin 107 if (!exitPending()) { 108 sleep(1); 109 } 110 111 return true; 112 } 113 114 void sendObjectAdded(MtpObjectHandle handle) { 115 mMutex.lock(); 116 if (mServer) 117 mServer->sendObjectAdded(handle); 118 mMutex.unlock(); 119 } 120 121 void sendObjectRemoved(MtpObjectHandle handle) { 122 mMutex.lock(); 123 if (mServer) 124 mServer->sendObjectRemoved(handle); 125 mMutex.unlock(); 126 } 127}; 128 129// This smart pointer is necessary for preventing MtpThread from exiting too early 130static sp<MtpThread> sThread; 131 132#endif // HAVE_ANDROID_OS 133 134static void 135android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, 136 jstring storagePath, jlong reserveSpace) 137{ 138#ifdef HAVE_ANDROID_OS 139 MtpDatabase* database = getMtpDatabase(env, javaDatabase); 140 const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL); 141 142 // create the thread and assign it to the smart pointer 143 MtpStorage* storage = new MtpStorage(MTP_FIRST_STORAGE_ID, storagePathStr, reserveSpace); 144 sThread = new MtpThread(database, storage); 145 146 env->ReleaseStringUTFChars(storagePath, storagePathStr); 147#endif 148} 149 150static void 151android_mtp_MtpServer_start(JNIEnv *env, jobject thiz) 152{ 153#ifdef HAVE_ANDROID_OS 154 MtpThread *thread = sThread.get(); 155 if (thread) 156 thread->run("MtpThread"); 157#endif // HAVE_ANDROID_OS 158} 159 160static void 161android_mtp_MtpServer_stop(JNIEnv *env, jobject thiz) 162{ 163#ifdef HAVE_ANDROID_OS 164 MtpThread *thread = sThread.get(); 165 if (thread) { 166 thread->requestExitAndWait(); 167 sThread = NULL; 168 } 169#endif 170} 171 172static void 173android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle) 174{ 175#ifdef HAVE_ANDROID_OS 176 MtpThread *thread = sThread.get(); 177 if (thread) 178 thread->sendObjectAdded(handle); 179#endif 180} 181 182static void 183android_mtp_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle) 184{ 185#ifdef HAVE_ANDROID_OS 186 MtpThread *thread = sThread.get(); 187 if (thread) 188 thread->sendObjectRemoved(handle); 189#endif 190} 191 192static void 193android_mtp_MtpServer_set_ptp_mode(JNIEnv *env, jobject thiz, jboolean usePtp) 194{ 195#ifdef HAVE_ANDROID_OS 196 MtpThread *thread = sThread.get(); 197 if (thread) 198 thread->setPtpMode(usePtp); 199#endif 200} 201 202// ---------------------------------------------------------------------------- 203 204static JNINativeMethod gMethods[] = { 205 {"native_setup", "(Landroid/mtp/MtpDatabase;Ljava/lang/String;J)V", 206 (void *)android_mtp_MtpServer_setup}, 207 {"native_start", "()V", (void *)android_mtp_MtpServer_start}, 208 {"native_stop", "()V", (void *)android_mtp_MtpServer_stop}, 209 {"native_send_object_added", "(I)V", (void *)android_mtp_MtpServer_send_object_added}, 210 {"native_send_object_removed", "(I)V", (void *)android_mtp_MtpServer_send_object_removed}, 211 {"native_set_ptp_mode", "(Z)V", (void *)android_mtp_MtpServer_set_ptp_mode}, 212}; 213 214static const char* const kClassPathName = "android/mtp/MtpServer"; 215 216int register_android_mtp_MtpServer(JNIEnv *env) 217{ 218 jclass clazz; 219 220 clazz = env->FindClass("android/mtp/MtpServer"); 221 if (clazz == NULL) { 222 LOGE("Can't find android/mtp/MtpServer"); 223 return -1; 224 } 225 226 return AndroidRuntime::registerNativeMethods(env, 227 "android/mtp/MtpServer", gMethods, NELEM(gMethods)); 228} 229