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