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