diff --git a/CMakeLists.txt b/CMakeLists.txt
index 06cd88b..5c09cfa 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.4.1)
-project(transmissionbtc VERSION 1.0.0 LANGUAGES C)
+project(transmissionbtc)
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type: [Debug|Release]" FORCE)
@@ -8,7 +8,7 @@ endif ()
set(EXT_C_FLAGS "-DANDROID -fno-unwind-tables -no-canonical-prefixes -D_FORTIFY_SOURCE=1 \
-D_FILE_OFFSET_BITS=64 -D_LARGE_FILES=1")
-set(EXT_CXX_FLAGS "${EXT_C_FLAGS} -fno-exceptions -fno-rtti")
+set(EXT_CXX_FLAGS "${EXT_C_FLAGS} -fno-exceptions -frtti -std=gnu++17")
set(EXT_EXE_LINKER_FLAGS "-Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a \
-Wl,--exclude-libs,libatomic.a -Wl,--gc-sections -static-libstdc++")
@@ -19,8 +19,10 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG ${EXT_EXE_LINKER_FLAGS})
else ()
set(EXT_C_FLAGS "-O3 -DNDEBUG -flto -fvisibility=hidden -fdata-sections -ffunction-sections ${EXT_C_FLAGS}")
- set(EXT_EXE_LINKER_FLAGS "-flto -O3 -Wl,--strip-all ${EXT_EXE_LINKER_FLAGS}")
+ set(EXT_CXX_FLAGS "-O3 -DNDEBUG -flto -fvisibility=hidden -fdata-sections -ffunction-sections ${EXT_CXX_FLAGS}")
+ set(EXT_EXE_LINKER_FLAGS "-fuse-ld=gold -flto -O3 -Wl,--strip-all ${EXT_EXE_LINKER_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE ${EXT_C_FLAGS})
+ set(CMAKE_CXX_FLAGS_RELEASE ${EXT_CXX_FLAGS})
set(CMAKE_EXE_LINKER_FLAGS_RELEASE ${EXT_EXE_LINKER_FLAGS})
endif ()
@@ -41,15 +43,15 @@ set(EXT_CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
)
add_library(${PROJECT_NAME} SHARED
- "${CMAKE_SOURCE_DIR}/src/main/cpp/native_to_java.c"
- "${CMAKE_SOURCE_DIR}/src/main/cpp/env.c"
- "${CMAKE_SOURCE_DIR}/src/main/cpp/sem.c"
- "${CMAKE_SOURCE_DIR}/src/main/cpp/curl.c"
- "${CMAKE_SOURCE_DIR}/src/main/cpp/hash.c"
- "${CMAKE_SOURCE_DIR}/src/main/cpp/commons.c"
- "${CMAKE_SOURCE_DIR}/src/main/cpp/torrent.c"
- "${CMAKE_SOURCE_DIR}/src/main/cpp/transmission.c"
- "${CMAKE_SOURCE_DIR}/src/main/cpp/stdredirect.c"
+ "${CMAKE_SOURCE_DIR}/src/main/cpp/native_to_java.cc"
+ "${CMAKE_SOURCE_DIR}/src/main/cpp/env.cc"
+ "${CMAKE_SOURCE_DIR}/src/main/cpp/sem.cc"
+ "${CMAKE_SOURCE_DIR}/src/main/cpp/curl.cc"
+ "${CMAKE_SOURCE_DIR}/src/main/cpp/hash.cc"
+ "${CMAKE_SOURCE_DIR}/src/main/cpp/commons.cc"
+ "${CMAKE_SOURCE_DIR}/src/main/cpp/torrent.cc"
+ "${CMAKE_SOURCE_DIR}/src/main/cpp/transmission.cc"
+ "${CMAKE_SOURCE_DIR}/src/main/cpp/stdredirect.cc"
)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
diff --git a/build.gradle b/build.gradle
index d2c29f2..734a21e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
-def version = '1.3.8'
-def versionNum = 9000017
+def version = '1.3.10'
+def versionNum = 9000020
def webInstallDir = "${project.buildDir}/web"
buildscript {
diff --git a/cmake/Transmission.cmake b/cmake/Transmission.cmake
index 54b6cb6..f03e6f8 100644
--- a/cmake/Transmission.cmake
+++ b/cmake/Transmission.cmake
@@ -11,6 +11,7 @@ set(TR_CMAKE_ARGS -DENABLE_TESTS=OFF -DENABLE_DAEMON=OFF -DINSTALL_DOC=OFF -DENA
set(TR_LIBRARIES
"${TR_BUILD_DIR}/libtransmission/libtransmission.a"
"${TR_BUILD_DIR}/third-party/dht/lib/libdht.a"
+ "${TR_BUILD_DIR}/third-party/arc4/src/libarc4.a"
"${TR_BUILD_DIR}/third-party/b64/lib/libb64.a"
"${TR_BUILD_DIR}/third-party/natpmp/lib/libnatpmp.a"
"${TR_BUILD_DIR}/third-party/miniupnpc/lib/libminiupnpc.a"
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 897cb97..5999e2c 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -9,7 +9,6 @@
-
@@ -18,7 +17,7 @@
+ tools:ignore="ermissions" />
-#include "transmission-private.h"
-
-jint throw(const char *file, int line, JNIEnv *env, const char *className,
- const char *format, ...) {
- int len;
- char msg[256];
- char *fmt = (char *) format;
-
-#ifndef NDEBUG
- char dfmt[1024];
- len = snprintf(dfmt, sizeof(dfmt), "[%s:%d] %s", file, line, format);
- if (len > 0) fmt = dfmt;
-#endif
-
- va_list args;
- va_start(args, format);
- len = vsnprintf(msg, sizeof(msg), (const char *) fmt, args);
- va_end(args);
- if (len > 0) fmt = msg;
-
- jclass c = (*env)->FindClass(env, className);
- if (c == NULL) (*env)->FindClass(env, "java/lang/Error");
-
- if ((*env)->ExceptionCheck(env)) {
- logErr("Exception: %s", fmt);
- } else {
- (*env)->ThrowNew(env, c, fmt);
- }
-
- return 0;
-}
-
-size_t cp(JNIEnv *env, const char *fromPath, const char *toPath) {
- FILE *from = NULL, *to = NULL;
- size_t count = 0;
-
- if ((from = fopen(fromPath, "rb")) == NULL) {
- throwIOEX(env, "Failed to open source file %s", fromPath);
- }
-
- if ((to = fopen(toPath, "wb")) == NULL) {
- fclose(from);
- throwIOEX(env, "Failed to open destination file %s", toPath);
- }
-
- size_t n;
- char buffer[BUFSIZ];
-
- while ((n = fread(buffer, sizeof(char), sizeof(buffer), from)) > 0) {
- if (fwrite(buffer, sizeof(char), n, to) != n) {
- throwIOEX(env, "Error writing to destination file %s", toPath);
- } else {
- count += n;
- }
- }
-
- CATCH:
- if (from != NULL) fclose(from);
- if (to != NULL) fclose(to);
- return count;
-}
-
-tr_ctor *ctorFromFile(JNIEnv *env, jlong jsession, jstring jpath, bool throwErr) {
- const tr_session *session = (const tr_session *) jsession;
- const char *path = (*env)->GetStringUTFChars(env, jpath, 0);
- tr_ctor *ctor = tr_ctorNew(session);
-
- if (tr_ctorSetMetainfoFromFile(ctor, path)) {
- tr_ctorFree(ctor);
- ctor = NULL;
- throwOrLog(env, CLASS_IOEX, throwErr, "Invalid torrent file: %s", path);
- }
-
- CATCH:
- (*env)->ReleaseStringUTFChars(env, jpath, path);
- return ctor;
-}
-
-tr_parse_result
-infoFromFile(JNIEnv *env, jlong jsession, jstring jpath, tr_info *info, bool throwErr) {
- const char *path = NULL;
- tr_parse_result result = TR_PARSE_ERR;
- tr_ctor *ctor = ctorFromFileEx(env, jsession, jpath);
- result = tr_torrentParse(ctor, info);
- tr_ctorFree(ctor);
-
- if (result != TR_PARSE_OK) {
- path = (*env)->GetStringUTFChars(env, jpath, 0);
- throwOrLog(env, CLASS_IOEX, throwErr, "Failed to parse torrent file: %s", path);
- }
-
- CATCH:
- if (path != NULL) (*env)->ReleaseStringUTFChars(env, jpath, path);
- return result;
-}
-
-int findTorrentByHash(tr_session *session, uint8_t *hash, Err *err) {
- tr_torrent *tor = tr_torrentFindFromHash(session, hash);
-
- if (tor == NULL) {
- char hashString[1 + 2 * SHA_DIGEST_LENGTH];
- tr_binary_to_hex(hash, hashString, SHA_DIGEST_LENGTH);
- err->set(err, CLASS_NSTEX, "No such torrent: hash=%s", hashString);
- return -1;
- }
-
- return tr_torrentId(tor);
-}
-
-tr_torrent *findTorrentById(tr_session *session, int id, Err *err) {
- tr_torrent *tor = tr_torrentFindFromId(session, id);
- if (tor == NULL) err->set(err, CLASS_NSTEX, "No such torrent: id=%d", id);
- return tor;
-}
-
-const tr_file *getFileInfo(tr_torrent *tor, uint32_t idx, Err *err) {
- const tr_info *info = tr_torrentInfo(tor);
- if (idx >= info->fileCount) {
- err->set(err, CLASS_IAEX, "Invalid file index: %d", idx);
- return NULL;
- } else {
- return &info->files[idx];
- }
-}
-
-const tr_file *getWantedFileInfo(tr_torrent *tor, uint32_t idx, Err *err) {
- const tr_file *f = getFileInfoEx(tor, idx, err);
- if (f->dnd != 0) {
- err->set(err, CLASS_IAEX, "File #%d is unwanted for download: %s", idx, f->name);
- return NULL;
- }
- CATCH:
- return f;
-}
-
-struct Future {
- Err err;
- sem_t sem;
- tr_session *session;
- void *userData;
- void *result;
- const char *ex;
- char *exMsg;
- uint16_t exMsgBufLen;
-
- void *(*func)(tr_session *session, void *userData, Err *err);
-};
-
-static void setError(struct Err *err, const char *ex, const char *msg, ...) {
- struct Future *f = (struct Future *) err;
-
- if (f->exMsg == NULL) {
- f->exMsgBufLen = 512;
- f->exMsg = calloc(f->exMsgBufLen, sizeof(char));
- }
-
- va_list args;
- va_start(args, msg);
- vsnprintf(f->exMsg, f->exMsgBufLen, msg, args);
- va_end(args);
- f->ex = ex;
- f->err.isSet = true;
-}
-
-static void runInEventThread(void *data) {
- struct Future *f = (struct Future *) data;
- f->result = f->func(f->session, f->userData, &(f->err));
- sem_post(&(f->sem));
-}
-
-void *runInTransmissionThread(const char *file, int line, JNIEnv *env, jlong jsession,
- void *(*func)(tr_session *, void *, Err *),
- void *userData) {
- struct Future f;
- sem_t *sem = &(f.sem);
- memset(sem, 0, sizeof(sem_t));
- sem_init(sem, 0, 0);
- f.session = (tr_session *) jsession;
- f.userData = userData;
- f.ex = NULL;
- f.exMsg = NULL;
- f.err.isSet = false;
- f.err.set = setError;
- f.func = func;
-
- tr_runInEventThread(f.session, runInEventThread, &f);
- while ((sem_wait(sem) == -1) && (errno == EINTR));
- sem_destroy(sem);
-
- if (f.err.isSet) {
- throw(file, line, env, f.ex, f.exMsg);
- free(f.exMsg);
- return NULL;
- } else {
- return f.result;
- }
-}
\ No newline at end of file
diff --git a/src/main/cpp/commons.cc b/src/main/cpp/commons.cc
new file mode 100644
index 0000000..5e9d58b
--- /dev/null
+++ b/src/main/cpp/commons.cc
@@ -0,0 +1,202 @@
+#include "commons.h"
+#include
+#include "transmission-private.h"
+
+extern "C" {
+
+jint throwException(const char *file, int line, JNIEnv *env, const char *className,
+ const char *format, ...) {
+ int len;
+ char msg[256];
+ char *fmt = (char *) format;
+
+#ifndef NDEBUG
+ char dfmt[1024];
+ len = snprintf(dfmt, sizeof(dfmt), "[%s:%d] %s", file, line, format);
+ if (len > 0) fmt = dfmt;
+#endif
+
+ va_list args;
+ va_start(args, format);
+ len = vsnprintf(msg, sizeof(msg), (const char *) fmt, args);
+ va_end(args);
+ if (len > 0) fmt = msg;
+
+ jclass c = env->FindClass(className);
+ if (c == NULL) env->FindClass("java/lang/Error");
+
+ if (env->ExceptionCheck()) {
+ logErr("Exception: %s", fmt);
+ } else {
+ env->ThrowNew(c, fmt);
+ }
+
+ return 0;
+}
+
+size_t cp(JNIEnv *env, const char *fromPath, const char *toPath) {
+ FILE *from = NULL, *to = NULL;
+ size_t count = 0;
+
+ if ((from = fopen(fromPath, "rb")) == NULL) {
+ throwIOEX(env, "Failed to open source file %s", fromPath);
+ }
+
+ if ((to = fopen(toPath, "wb")) == NULL) {
+ fclose(from);
+ throwIOEX(env, "Failed to open destination file %s", toPath);
+ }
+
+ size_t n;
+ char buffer[BUFSIZ];
+
+ while ((n = fread(buffer, sizeof(char), sizeof(buffer), from)) > 0) {
+ if (fwrite(buffer, sizeof(char), n, to) != n) {
+ throwIOEX(env, "Error writing to destination file %s", toPath);
+ } else {
+ count += n;
+ }
+ }
+
+ CATCH:
+ if (from != NULL) fclose(from);
+ if (to != NULL) fclose(to);
+ return count;
+}
+
+tr_ctor *ctorFromFile(JNIEnv *env, jlong jsession, jstring jpath, bool throwErr) {
+ const tr_session *session = (const tr_session *) jsession;
+ const char *path = env->GetStringUTFChars(jpath, 0);
+ tr_ctor *ctor = tr_ctorNew(session);
+
+ if (tr_ctorSetMetainfoFromFile(ctor, path)) {
+ tr_ctorFree(ctor);
+ ctor = NULL;
+ throwOrLog(env, CLASS_IOEX, throwErr, "Invalid torrent file: %s", path);
+ }
+
+ CATCH:
+ env->ReleaseStringUTFChars(jpath, path);
+ return ctor;
+}
+
+tr_parse_result
+infoFromFile(JNIEnv *env, jlong jsession, jstring jpath, tr_info *info, bool throwErr) {
+ const char *path = NULL;
+ tr_parse_result result = TR_PARSE_ERR;
+ tr_ctor *ctor = ctorFromFileEx(env, jsession, jpath);
+ result = tr_torrentParse(ctor, info);
+ tr_ctorFree(ctor);
+
+ if (result != TR_PARSE_OK) {
+ path = env->GetStringUTFChars(jpath, 0);
+ throwOrLog(env, CLASS_IOEX, throwErr, "Failed to parse torrent file: %s", path);
+ }
+
+ CATCH:
+ if (path != NULL) env->ReleaseStringUTFChars(jpath, path);
+ return result;
+}
+
+int findTorrentByHash(tr_session *session, uint8_t *hash, Err *err) {
+ tr_torrent *tor = tr_torrentFindFromHash(session, hash);
+
+ if (tor == NULL) {
+ char hashString[1 + 2 * SHA_DIGEST_LENGTH];
+ tr_binary_to_hex(hash, hashString, SHA_DIGEST_LENGTH);
+ err->set(err, CLASS_NSTEX, "No such torrent: hash=%s", hashString);
+ return -1;
+ }
+
+ return tr_torrentId(tor);
+}
+
+tr_torrent *findTorrentById(tr_session *session, int id, Err *err) {
+ tr_torrent *tor = tr_torrentFindFromId(session, id);
+ if (tor == NULL) err->set(err, CLASS_NSTEX, "No such torrent: id=%d", id);
+ return tor;
+}
+
+const tr_file *getFileInfo(tr_torrent *tor, uint32_t idx, Err *err) {
+ const tr_info *info = tr_torrentInfo(tor);
+ if (idx >= info->fileCount) {
+ err->set(err, CLASS_IAEX, "Invalid file index: %d", idx);
+ return NULL;
+ } else {
+ return &info->files[idx];
+ }
+}
+
+const tr_file *getWantedFileInfo(tr_torrent *tor, uint32_t idx, Err *err) {
+ const tr_file *f = getFileInfoEx(tor, idx, err);
+ if (f->dnd != 0) {
+ err->set(err, CLASS_IAEX, "File #%d is unwanted for download: %s", idx, f->name);
+ return NULL;
+ }
+ CATCH:
+ return f;
+}
+
+struct Future {
+ Err err;
+ sem_t sem;
+ tr_session *session;
+ void *userData;
+ void *result;
+ const char *ex;
+ char *exMsg;
+ uint16_t exMsgBufLen;
+
+ void *(*func)(tr_session *session, void *userData, Err *err);
+};
+
+static void setError(struct Err *err, const char *ex, const char *msg, ...) {
+ struct Future *f = (struct Future *) err;
+
+ if (f->exMsg == NULL) {
+ f->exMsgBufLen = 512;
+ f->exMsg = (char *) calloc(f->exMsgBufLen, sizeof(char));
+ }
+
+ va_list args;
+ va_start(args, msg);
+ vsnprintf(f->exMsg, f->exMsgBufLen, msg, args);
+ va_end(args);
+ f->ex = ex;
+ f->err.isSet = true;
+}
+
+static void runInEventThread(void *data) {
+ struct Future *f = (struct Future *) data;
+ f->result = f->func(f->session, f->userData, &(f->err));
+ sem_post(&(f->sem));
+}
+
+void *runInTransmissionThread(const char *file, int line, JNIEnv *env, jlong jsession,
+ void *(*func)(tr_session *, void *, Err *),
+ void *userData) {
+ struct Future f;
+ sem_t *sem = &(f.sem);
+ memset(sem, 0, sizeof(sem_t));
+ sem_init(sem, 0, 0);
+ f.session = (tr_session *) jsession;
+ f.userData = userData;
+ f.ex = NULL;
+ f.exMsg = NULL;
+ f.err.isSet = false;
+ f.err.set = setError;
+ f.func = func;
+
+ tr_runInEventThread(f.session, runInEventThread, &f);
+ while ((sem_wait(sem) == -1) && (errno == EINTR));
+ sem_destroy(sem);
+
+ if (f.err.isSet) {
+ throwException(file, line, env, f.ex, f.exMsg);
+ free(f.exMsg);
+ return NULL;
+ } else {
+ return f.result;
+ }
+}
+} // extern "C"
\ No newline at end of file
diff --git a/src/main/cpp/commons.h b/src/main/cpp/commons.h
index 73a22a5..2c8cca2 100644
--- a/src/main/cpp/commons.h
+++ b/src/main/cpp/commons.h
@@ -11,6 +11,8 @@
#include
#include
+extern "C" {
+
#define LOG_TAG "transmissionbtc"
#define CLASS_IOEX "java/io/IOException"
#define CLASS_IAEX "java/lang/IllegalArgumentException"
@@ -21,7 +23,7 @@
#define logErr(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
-#define throwEX(env, className, ...) { throw(__FILENAME__, __LINE__, env, className, __VA_ARGS__); goto CATCH; }
+#define throwEX(env, className, ...) { throwException(__FILENAME__, __LINE__, env, className, __VA_ARGS__); goto CATCH; }
#define throwIOEX(env, ...) throwEX(env, CLASS_IOEX, __VA_ARGS__)
#define throwOrLog(env, className, throw, ...) \
if (throw) { throwEX(env, className, __VA_ARGS__); } \
@@ -34,18 +36,18 @@ typedef struct Err {
} Err;
#define errCheck(err) if (err->isSet) goto CATCH
-jint throw(const char *, int, JNIEnv *, const char *, const char *, ...);
+jint throwException(const char *, int, JNIEnv *, const char *, const char *, ...);
size_t cp(JNIEnv *env, const char *, const char *);
#define ctorFromFileEx(env, jsession, jpath) \
ctorFromFile(env, jsession, jpath, true); \
- if ((*env)->ExceptionCheck(env)) goto CATCH
+ if (env->ExceptionCheck()) goto CATCH
tr_ctor *ctorFromFile(JNIEnv *, jlong, jstring, bool);
#define infoFromFileEx(env, jsession, jpath, info) \
- if ((infoFromFile(env, jsession, jpath, info, true) != TR_PARSE_OK) || (*env)->ExceptionCheck(env)) \
+ if ((infoFromFile(env, jsession, jpath, info, true) != TR_PARSE_OK) || env->ExceptionCheck()) \
goto CATCH
tr_parse_result infoFromFile(JNIEnv *, jlong, jstring, tr_info *, bool);
@@ -68,10 +70,11 @@ const tr_file *getWantedFileInfo(tr_torrent *tor, uint32_t idx, Err *err);
#define runInTransmissionThreadEx(env, jsession, func, data) \
runInTransmissionThread(__FILENAME__, __LINE__, env, jsession, func, data);\
- if ((*env)->ExceptionCheck(env)) goto CATCH
+ if (env->ExceptionCheck()) goto CATCH
void *runInTransmissionThread(const char *file, int line, JNIEnv *env, jlong jsession,
void *(*func)(tr_session *session, void *userData, Err *err),
void *userData);
+} // extern "C"
#endif //TRANSMISSIONBTC_COMMONS_H
diff --git a/src/main/cpp/curl.c b/src/main/cpp/curl.cc
similarity index 86%
rename from src/main/cpp/curl.c
rename to src/main/cpp/curl.cc
index 29fb911..5147c57 100644
--- a/src/main/cpp/curl.c
+++ b/src/main/cpp/curl.cc
@@ -5,12 +5,14 @@
#include
#include
+extern "C" {
JNIEXPORT void JNICALL
Java_com_ap_transmission_btc_Native_curl(JNIEnv *env, jclass __unused c,
jstring jurl, jstring jdst, jint timeout) {
- const char *dst = (*env)->GetStringUTFChars(env, jdst, 0);
+ const char *dst = env->GetStringUTFChars(jdst, 0);
FILE *file = fopen(dst, "wb");
+{
if (file == NULL) {
throwEX(env, CLASS_IOEX, "Failed to open file %s: %s", dst, strerror(errno));
}
@@ -19,7 +21,7 @@ Java_com_ap_transmission_btc_Native_curl(JNIEnv *env, jclass __unused c,
if (curl) {
char err[CURL_ERROR_SIZE];
- const char *url = (*env)->GetStringUTFChars(env, jurl, 0);
+ const char *url = env->GetStringUTFChars(jurl, 0);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
@@ -36,7 +38,7 @@ Java_com_ap_transmission_btc_Native_curl(JNIEnv *env, jclass __unused c,
#endif
CURLcode res = curl_easy_perform(curl);
- (*env)->ReleaseStringUTFChars(env, jurl, url);
+ env->ReleaseStringUTFChars(jurl, url);
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
@@ -46,7 +48,8 @@ Java_com_ap_transmission_btc_Native_curl(JNIEnv *env, jclass __unused c,
throwEX(env, CLASS_IOEX, "Failed to initialize curl");
}
- CATCH:
- (*env)->ReleaseStringUTFChars(env, jdst, dst);
+} CATCH:
+ env->ReleaseStringUTFChars(jdst, dst);
if (file != NULL) fclose(file);
}
+} //extern "C"
\ No newline at end of file
diff --git a/src/main/cpp/env.c b/src/main/cpp/env.c
deleted file mode 100644
index 1bffa93..0000000
--- a/src/main/cpp/env.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "commons.h"
-#include
-
-JNIEXPORT void JNICALL
-Java_com_ap_transmission_btc_Native_envUnset(
- JNIEnv *env, jclass __unused c, jstring jname) {
- const char *name = (*env)->GetStringUTFChars(env, jname, 0);
- unsetenv(name);
- (*env)->ReleaseStringUTFChars(env, jname, name);
-}
-
-JNIEXPORT void JNICALL
-Java_com_ap_transmission_btc_Native_envSet(
- JNIEnv *env, jclass __unused c, jstring jname, jstring jvalue) {
- if ((jvalue == NULL) || ((*env)->GetStringUTFLength(env, jvalue) == 0)) {
- Java_com_ap_transmission_btc_Native_envUnset(env, c, jname);
- } else {
- const char *name = (*env)->GetStringUTFChars(env, jname, 0);
- const char *value = (*env)->GetStringUTFChars(env, jvalue, 0);
- setenv(name, value, 1);
- (*env)->ReleaseStringUTFChars(env, jname, name);
- (*env)->ReleaseStringUTFChars(env, jvalue, value);
- }
-}
\ No newline at end of file
diff --git a/src/main/cpp/env.cc b/src/main/cpp/env.cc
new file mode 100644
index 0000000..4cadf73
--- /dev/null
+++ b/src/main/cpp/env.cc
@@ -0,0 +1,26 @@
+#include "commons.h"
+#include
+
+extern "C" {
+JNIEXPORT void JNICALL
+Java_com_ap_transmission_btc_Native_envUnset(
+ JNIEnv *env, jclass __unused c, jstring jname) {
+ const char *name = env->GetStringUTFChars(jname, 0);
+ unsetenv(name);
+ env->ReleaseStringUTFChars(jname, name);
+}
+
+JNIEXPORT void JNICALL
+Java_com_ap_transmission_btc_Native_envSet(
+ JNIEnv *env, jclass __unused c, jstring jname, jstring jvalue) {
+ if ((jvalue == NULL) || (env->GetStringUTFLength(jvalue) == 0)) {
+ Java_com_ap_transmission_btc_Native_envUnset(env, c, jname);
+ } else {
+ const char *name = env->GetStringUTFChars(jname, 0);
+ const char *value = env->GetStringUTFChars(jvalue, 0);
+ setenv(name, value, 1);
+ env->ReleaseStringUTFChars(jname, name);
+ env->ReleaseStringUTFChars(jvalue, value);
+ }
+}
+} // extern "C"
\ No newline at end of file
diff --git a/src/main/cpp/hash.c b/src/main/cpp/hash.cc
similarity index 56%
rename from src/main/cpp/hash.c
rename to src/main/cpp/hash.cc
index 183577a..7466600 100644
--- a/src/main/cpp/hash.c
+++ b/src/main/cpp/hash.cc
@@ -2,6 +2,7 @@
#include
#include
+extern "C" {
JNIEXPORT jint JNICALL
Java_com_ap_transmission_btc_Native_hashLength(JNIEnv *__unused env, jclass __unused c) {
return SHA_DIGEST_LENGTH;
@@ -12,32 +13,34 @@ Java_com_ap_transmission_btc_Native_hashBytesToString(
JNIEnv *env, jclass __unused c, jbyteArray jhash) {
char hash[SHA_DIGEST_LENGTH];
char hashString[1 + 2 * SHA_DIGEST_LENGTH];
- (*env)->GetByteArrayRegion(env, jhash, 0, sizeof(hash), (jbyte *) hash);
+ env->GetByteArrayRegion(jhash, 0, sizeof(hash), (jbyte *) hash);
tr_binary_to_hex(hash, hashString, sizeof(hash));
- return (*env)->NewStringUTF(env, (const char *) hashString);
+ return env->NewStringUTF((const char *) hashString);
}
JNIEXPORT jbyteArray JNICALL
Java_com_ap_transmission_btc_Native_hashStringToBytes(
JNIEnv *env, jclass __unused c, jstring jhashString) {
char hash[SHA_DIGEST_LENGTH];
- const char *hashString = (*env)->GetStringUTFChars(env, jhashString, 0);
+ const char *hashString = env->GetStringUTFChars(jhashString, 0);
tr_hex_to_binary(hashString, hash, sizeof(hash));
- (*env)->ReleaseStringUTFChars(env, jhashString, hashString);
- jbyteArray jhash = (*env)->NewByteArray(env, sizeof(hash));
- (*env)->SetByteArrayRegion(env, jhash, 0, sizeof(hash), (const jbyte *) hash);
+ env->ReleaseStringUTFChars(jhashString, hashString);
+ jbyteArray jhash = env->NewByteArray(sizeof(hash));
+ env->SetByteArrayRegion(jhash, 0, sizeof(hash), (const jbyte *) hash);
return jhash;
}
JNIEXPORT jbyteArray JNICALL
Java_com_ap_transmission_btc_Native_hashGetTorrentHash(
JNIEnv *env, jclass __unused c, jstring torrentPath) {
- tr_info info;
- infoFromFileEx(env, 0, torrentPath, &info);
- jbyteArray jhash = (*env)->NewByteArray(env, SHA_DIGEST_LENGTH);
- (*env)->SetByteArrayRegion(env, jhash, 0, SHA_DIGEST_LENGTH, (const jbyte *) info.hash);
- tr_metainfoFree(&info);
- return jhash;
- CATCH:
- return NULL;
+{
+ tr_info info;
+ infoFromFileEx(env, 0, torrentPath, &info);
+ jbyteArray jhash = env->NewByteArray(SHA_DIGEST_LENGTH);
+ env->SetByteArrayRegion(jhash, 0, SHA_DIGEST_LENGTH, (const jbyte *) info.hash);
+ tr_metainfoFree(&info);
+ return jhash;
+} CATCH:
+ return NULL;
}
+} // extern "C"
\ No newline at end of file
diff --git a/src/main/cpp/native_to_java.c b/src/main/cpp/native_to_java.cc
similarity index 56%
rename from src/main/cpp/native_to_java.c
rename to src/main/cpp/native_to_java.cc
index 4dff322..0600d72 100644
--- a/src/main/cpp/native_to_java.c
+++ b/src/main/cpp/native_to_java.cc
@@ -7,6 +7,9 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
+
+extern "C" {
+
static JavaVM *jvm;
static jclass classNative;
static jclass classStorageAccess;
@@ -21,86 +24,91 @@ static jmethodID renamePath;
static jmethodID removePath;
#define JvmAttach() \
- JNIEnv *env;\
- jint _detached = (*jvm)->GetEnv(jvm, &env, JNI_VERSION_1_2);\
- if ((_detached == JNI_EDETACHED) && ((*jvm)->AttachCurrentThread(jvm, &env, NULL) != JNI_OK)) {\
+ void *__env__ = nullptr;\
+ jint _detached = jvm->GetEnv(&__env__, JNI_VERSION_1_2);\
+ JNIEnv *env = (JNIEnv*) __env__;\
+ if ((_detached == JNI_EDETACHED) && (jvm->AttachCurrentThread(&env, NULL) != JNI_OK)) {\
logErr("JavaVM->AttachCurrentThread() failed");\
goto CATCH;\
}
#define JvmDetach() \
- if ((*env)->ExceptionCheck(env)) logErr("Native to Java call failed");\
- if (_detached == JNI_EDETACHED) (*jvm)->DetachCurrentThread(jvm)
+ if (env->ExceptionCheck()) logErr("Native to Java call failed");\
+ if (_detached == JNI_EDETACHED) jvm->DetachCurrentThread()
static void rndChars(char *str, int num);
JNIEXPORT void JNICALL
Java_com_ap_transmission_btc_Native_nativeToJavaInit(JNIEnv *env, jclass c) {
- (*env)->GetJavaVM(env, &jvm);
- jclass sa = (*env)->FindClass(env, "com/ap/transmission/btc/StorageAccess");
- classNative = (jclass) (*env)->NewGlobalRef(env, c);
- classStorageAccess = (jclass) (*env)->NewGlobalRef(env, sa);
- addedOrChangedCallback = (*env)->GetStaticMethodID(env, c, "torrentAddedOrChangedCallback",
+ env->GetJavaVM(&jvm);
+ jclass sa = env->FindClass("com/ap/transmission/btc/StorageAccess");
+ classNative = (jclass) env->NewGlobalRef(c);
+ classStorageAccess = (jclass) env->NewGlobalRef(sa);
+ addedOrChangedCallback = env->GetStaticMethodID(c, "torrentAddedOrChangedCallback",
"()V");
- stoppedCallback = (*env)->GetStaticMethodID(env, c, "torrentStoppedCallback", "()V");
- sessionChangedCallback = (*env)->GetStaticMethodID(env, c, "sessionChangedCallback", "()V");
- scheduledAltSpeedCallback = (*env)->GetStaticMethodID(env, c, "scheduledAltSpeedCallback", "()V");
- createDir = (*env)->GetStaticMethodID(env, sa, "createDir", "(Ljava/lang/String;)Z");
- openFile = (*env)->GetStaticMethodID(env, sa, "openFile", "(Ljava/lang/String;ZZZ)I");
- closeFileDescriptor = (*env)->GetStaticMethodID(env, sa, "closeFileDescriptor", "(I)Z");
- renamePath = (*env)->GetStaticMethodID(env, sa, "renamePath",
+ stoppedCallback = env->GetStaticMethodID(c, "torrentStoppedCallback", "()V");
+ sessionChangedCallback = env->GetStaticMethodID(c, "sessionChangedCallback", "()V");
+ scheduledAltSpeedCallback = env->GetStaticMethodID(c, "scheduledAltSpeedCallback", "()V");
+ createDir = env->GetStaticMethodID(sa, "createDir", "(Ljava/lang/String;)Z");
+ openFile = env->GetStaticMethodID(sa, "openFile", "(Ljava/lang/String;ZZZ)I");
+ closeFileDescriptor = env->GetStaticMethodID(sa, "closeFileDescriptor", "(I)Z");
+ renamePath = env->GetStaticMethodID(sa, "renamePath",
"(Ljava/lang/String;Ljava/lang/String;)Z");
- removePath = (*env)->GetStaticMethodID(env, sa, "removePath", "(Ljava/lang/String;)Z");
+ removePath = env->GetStaticMethodID(sa, "removePath", "(Ljava/lang/String;)Z");
}
void callAddedOrChangedCallback() {
JvmAttach();
- (*env)->CallStaticVoidMethod(env, classNative, addedOrChangedCallback);
+ env->CallStaticVoidMethod(classNative, addedOrChangedCallback);
JvmDetach();
CATCH:;
}
void callStoppedCallback() {
JvmAttach();
- (*env)->CallStaticVoidMethod(env, classNative, stoppedCallback);
+ env->CallStaticVoidMethod(classNative, stoppedCallback);
JvmDetach();
CATCH:;
}
void callSessionChangedCallback() {
JvmAttach();
- (*env)->CallStaticVoidMethod(env, classNative, sessionChangedCallback);
+ env->CallStaticVoidMethod(classNative, sessionChangedCallback);
JvmDetach();
CATCH:;
}
void callScheduledAltSpeedCallback() {
JvmAttach();
- (*env)->CallStaticVoidMethod(env, classNative, scheduledAltSpeedCallback);
+ env->CallStaticVoidMethod(classNative, scheduledAltSpeedCallback);
JvmDetach();
CATCH:;
}
+} // extern "C"
+
bool tr_android_dir_create(char const *path) {
jboolean result = JNI_FALSE;
+{
JvmAttach();
- jstring jpath = (*env)->NewStringUTF(env, path);
- result = (*env)->CallStaticBooleanMethod(env, classStorageAccess, createDir, jpath);
+ jstring jpath = env->NewStringUTF(path);
+ result = env->CallStaticBooleanMethod(classStorageAccess, createDir, jpath);
JvmDetach();
- CATCH:
+} CATCH:
return result == JNI_TRUE;
}
tr_sys_file_t tr_android_file_open(char const *path, int flags) {
int fd = -1;
+{
JvmAttach();
jboolean create = (flags & (TR_SYS_FILE_CREATE | TR_SYS_FILE_CREATE_NEW)) ? JNI_TRUE : JNI_FALSE;
jboolean writable = JNI_TRUE;
jboolean truncate = (flags & TR_SYS_FILE_TRUNCATE) ? JNI_TRUE : JNI_FALSE;
- jstring jpath = (*env)->NewStringUTF(env, path);
- fd = (*env)->CallStaticIntMethod(env, classStorageAccess, openFile, jpath,
+ jstring jpath = env->NewStringUTF(path);
+ fd = env->CallStaticIntMethod(classStorageAccess, openFile, jpath,
create, writable, truncate);
JvmDetach();
- CATCH:
+} CATCH:
return (fd == -1) ? TR_BAD_SYS_FILE : fd;
}
@@ -122,32 +130,35 @@ tr_sys_file_t tr_android_file_open_temp(char *path_template) {
bool tr_android_file_close(tr_sys_file_t handle) {
jboolean result = JNI_FALSE;
+{
JvmAttach();
- result = (*env)->CallStaticBooleanMethod(env, classStorageAccess, closeFileDescriptor,
+ result = env->CallStaticBooleanMethod(classStorageAccess, closeFileDescriptor,
(jint) handle);
JvmDetach();
- CATCH:
+} CATCH:
return result == JNI_TRUE;
}
bool tr_android_path_rename(char const *src_path, char const *dst_path) {
jboolean result = JNI_FALSE;
+{
JvmAttach();
- jstring src = (*env)->NewStringUTF(env, src_path);
- jstring dst = (*env)->NewStringUTF(env, dst_path);
- result = (*env)->CallStaticBooleanMethod(env, classStorageAccess, renamePath, src, dst);
+ jstring src = env->NewStringUTF(src_path);
+ jstring dst = env->NewStringUTF(dst_path);
+ result = env->CallStaticBooleanMethod(classStorageAccess, renamePath, src, dst);
JvmDetach();
- CATCH:
+} CATCH:
return result == JNI_TRUE;
}
bool tr_android_path_remove(char const *path) {
jboolean result = JNI_FALSE;
+{
JvmAttach();
- jstring jpath = (*env)->NewStringUTF(env, path);
- result = (*env)->CallStaticBooleanMethod(env, classStorageAccess, removePath, jpath);
+ jstring jpath = env->NewStringUTF(path);
+ result = env->CallStaticBooleanMethod(classStorageAccess, removePath, jpath);
JvmDetach();
- CATCH:
+} CATCH:
return result == JNI_TRUE;
}
diff --git a/src/main/cpp/native_to_java.h b/src/main/cpp/native_to_java.h
index 85d8b83..6e722c9 100644
--- a/src/main/cpp/native_to_java.h
+++ b/src/main/cpp/native_to_java.h
@@ -5,6 +5,7 @@
#ifndef TRANSMISSIONBTC_NATIVE_TO_JAVA_H
#define TRANSMISSIONBTC_NATIVE_TO_JAVA_H
+extern "C" {
void callAddedOrChangedCallback();
void callStoppedCallback();
@@ -13,4 +14,5 @@ void callSessionChangedCallback();
void callScheduledAltSpeedCallback();
+} // extern "C"
#endif //TRANSMISSIONBTC_NATIVE_TO_JAVA_H
diff --git a/src/main/cpp/sem.c b/src/main/cpp/sem.c
deleted file mode 100644
index 02e4b72..0000000
--- a/src/main/cpp/sem.c
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "commons.h"
-#include
-#include
-
-JNIEXPORT jlong JNICALL
-Java_com_ap_transmission_btc_Native_semCreate(JNIEnv *__unused env, jclass __unused c) {
- sem_t *sem = malloc(sizeof(sem_t));
- memset(sem, 0, sizeof(sem_t));
- sem_init(sem, 0, 0);
- return (jlong) sem;
-}
-
-JNIEXPORT void JNICALL
-Java_com_ap_transmission_btc_Native_semDestroy(JNIEnv *__unused env, jclass __unused c,
- jlong jsem) {
- sem_t *sem = (sem_t *) jsem;
- sem_destroy(sem);
- free(sem);
-}
-
-JNIEXPORT void JNICALL
-Java_com_ap_transmission_btc_Native_semPost(JNIEnv *__unused env, jclass __unused c,
- jlong jsem) {
- sem_t *sem = (sem_t *) jsem;
- sem_post(sem);
-}
\ No newline at end of file
diff --git a/src/main/cpp/sem.cc b/src/main/cpp/sem.cc
new file mode 100644
index 0000000..d3fba35
--- /dev/null
+++ b/src/main/cpp/sem.cc
@@ -0,0 +1,38 @@
+#include "commons.h"
+#include
+#include
+
+extern "C" {
+
+JNIEXPORT jlong
+JNICALL
+Java_com_ap_transmission_btc_Native_semCreate(JNIEnv *__unused env, jclass __unused c) {
+ sem_t *sem = (sem_t *) malloc(sizeof(sem_t));
+ memset(sem, 0, sizeof(sem_t));
+ sem_init(sem, 0, 0);
+ return (jlong)
+ sem;
+}
+
+JNIEXPORT void JNICALL
+Java_com_ap_transmission_btc_Native_semDestroy(JNIEnv * __unused env, jclass
+__unused c,
+ jlong
+jsem ) {
+sem_t *sem = (sem_t *) jsem;
+sem_destroy(sem);
+free(sem);
+}
+
+JNIEXPORT void JNICALL
+Java_com_ap_transmission_btc_Native_semPost(JNIEnv
+*
+__unused env, jclass
+__unused c,
+ jlong
+jsem) {
+sem_t *sem = (sem_t *) jsem;
+sem_post(sem);
+}
+
+} // extern "C"
\ No newline at end of file
diff --git a/src/main/cpp/stdredirect.c b/src/main/cpp/stdredirect.cc
similarity index 97%
rename from src/main/cpp/stdredirect.c
rename to src/main/cpp/stdredirect.cc
index a314ae6..4a1dc14 100644
--- a/src/main/cpp/stdredirect.c
+++ b/src/main/cpp/stdredirect.cc
@@ -11,6 +11,8 @@ Java_com_ap_transmission_btc_Native_stdRedirect(JNIEnv *__unused env, jclass __u
#include
#include
+extern "C" {
+
static int pfd[2];
static pthread_t thr;
@@ -43,4 +45,5 @@ Java_com_ap_transmission_btc_Native_stdRedirect(JNIEnv *env, jclass __unused c)
CATCH:;
}
+} // extern "C"
#endif
\ No newline at end of file
diff --git a/src/main/cpp/torrent.c b/src/main/cpp/torrent.cc
similarity index 82%
rename from src/main/cpp/torrent.c
rename to src/main/cpp/torrent.cc
index c663704..f35f0f1 100644
--- a/src/main/cpp/torrent.c
+++ b/src/main/cpp/torrent.cc
@@ -2,6 +2,7 @@
#include "transmission-private.h"
#include
+extern "C" {
// ----------------------------------- torrentAdd --------------------------------------------------
/*
* Returns:
@@ -15,26 +16,26 @@ Java_com_ap_transmission_btc_Native_torrentAdd(
JNIEnv *env, jclass __unused c, jlong jsession, jstring jpath, jstring jdownloadDir,
jboolean setDelete, jboolean sequential, jintArray unwantedIndexes,
jbyteArray returnMeTorrentHash) {
- bool delete = false;
+ bool del = false;
tr_ctor *ctor = ctorFromFile(env, jsession, jpath, false);
if (ctor != NULL) {
if (jdownloadDir != NULL) {
- const char *downloadDir = (*env)->GetStringUTFChars(env, jdownloadDir, 0);
+ const char *downloadDir = env->GetStringUTFChars(jdownloadDir, 0);
tr_ctorSetDownloadDir(ctor, TR_FORCE, downloadDir);
- (*env)->ReleaseStringUTFChars(env, jdownloadDir, downloadDir);
+ env->ReleaseStringUTFChars(jdownloadDir, downloadDir);
}
if (unwantedIndexes != NULL) {
- jsize len = (*env)->GetArrayLength(env, unwantedIndexes);
- jint *unwanted = (*env)->GetIntArrayElements(env, unwantedIndexes, 0);
+ jsize len = env->GetArrayLength(unwantedIndexes);
+ jint *unwanted = env->GetIntArrayElements(unwantedIndexes, 0);
tr_file_index_t trIndexes[len];
for (int i = 0; i < len; i++) {
trIndexes[i] = (tr_file_index_t) unwanted[i];
}
- (*env)->ReleaseIntArrayElements(env, unwantedIndexes, unwanted, 0);
+ env->ReleaseIntArrayElements(unwantedIndexes, unwanted, 0);
tr_ctorSetFilesWanted(ctor, trIndexes, (tr_file_index_t) len, false);
}
@@ -54,8 +55,8 @@ Java_com_ap_transmission_btc_Native_torrentAdd(
const tr_info *info = tr_torrentInfo(tor);
tr_file_index_t wanted[info->fileCount];
tr_file_index_t wantedCount = 0;
- jsize len = (*env)->GetArrayLength(env, unwantedIndexes);
- jint *unwanted = (*env)->GetIntArrayElements(env, unwantedIndexes, 0);
+ jsize len = env->GetArrayLength(unwantedIndexes);
+ jint *unwanted = env->GetIntArrayElements(unwantedIndexes, 0);
for (int i = 0; i < info->fileCount; i++) {
tr_file f = info->files[i];
@@ -72,23 +73,23 @@ Java_com_ap_transmission_btc_Native_torrentAdd(
if (w) wanted[wantedCount++] = (tr_file_index_t) i;
}
- (*env)->ReleaseIntArrayElements(env, unwantedIndexes, unwanted, 0);
+ env->ReleaseIntArrayElements(unwantedIndexes, unwanted, 0);
if (wantedCount != 0) {
tr_torrentSetFileDLs(tor, wanted, wantedCount, true);
- err = (tr_ctorGetDeleteSource(ctor, &delete) && delete) ? 3 : 0;
+ err = (tr_ctorGetDeleteSource(ctor, &del) && del) ? 3 : 0;
}
}
} else {
err = 1;
}
} else if (!err) {
- err = (tr_ctorGetDeleteSource(ctor, &delete) && delete) ? 3 : 0;
+ err = (tr_ctorGetDeleteSource(ctor, &del) && del) ? 3 : 0;
}
if ((returnMeTorrentHash != NULL) && (tor != NULL)) {
const tr_info *info = tr_torrentInfo(tor);
- (*env)->SetByteArrayRegion(env, returnMeTorrentHash, 0, SHA_DIGEST_LENGTH,
+ env->SetByteArrayRegion(returnMeTorrentHash, 0, SHA_DIGEST_LENGTH,
(const jbyte *) info->hash);
}
@@ -125,7 +126,7 @@ Java_com_ap_transmission_btc_Native_torrentRemove(
// ------------------------------------ torrentStop ------------------------------------------------
static void *torrentStop(tr_session *session, void *data, Err *err) {
- tr_torrent *tor = findTorrentByIdEx(session, (int) data, err);
+ tr_torrent *tor = findTorrentByIdEx(session, (int) (intptr_t) data, err);
tr_torrentStop(tor);
CATCH:
return NULL;
@@ -141,7 +142,7 @@ Java_com_ap_transmission_btc_Native_torrentStop(
// ------------------------------------ torrentStart -----------------------------------------------
static void *torrentStart(tr_session *session, void *data, Err *err) {
- tr_torrent *tor = findTorrentByIdEx(session, (int) data, err);
+ tr_torrent *tor = findTorrentByIdEx(session, (int) (intptr_t) data, err);
tr_torrentStart(tor);
CATCH:
return NULL;
@@ -157,7 +158,7 @@ Java_com_ap_transmission_btc_Native_torrentStart(
// ------------------------------------ torrentVerify -----------------------------------------------
static void *torrentVerify(tr_session *session, void *data, Err *err) {
- tr_torrent *tor = findTorrentByIdEx(session, (int) data, err);
+ tr_torrent *tor = findTorrentByIdEx(session, (int) (intptr_t) data, err);
tr_torrentVerify(tor, NULL, NULL);
CATCH:
return NULL;
@@ -175,22 +176,23 @@ Java_com_ap_transmission_btc_Native_torrentVerify(
JNIEXPORT jobjectArray JNICALL
Java_com_ap_transmission_btc_Native_torrentListFilesFromFile(
JNIEnv *env, jclass __unused c, jstring jtorrent) {
+{
tr_info info;
infoFromFileEx(env, 0, jtorrent, &info);
tr_file_index_t count = info.fileCount;
tr_file *files = info.files;
- jclass jstring = (*env)->FindClass(env, "java/lang/String");
- jobjectArray a = (*env)->NewObjectArray(env, count, jstring, NULL);
+ jclass jstring = env->FindClass("java/lang/String");
+ jobjectArray a = env->NewObjectArray(count, jstring, NULL);
for (int i = 0; i < count; i++) {
- jobject jname = (*env)->NewStringUTF(env, files[i].name);
- (*env)->SetObjectArrayElement(env, a, i, jname);
- (*env)->DeleteLocalRef(env, jname);
+ jobject jname = env->NewStringUTF(files[i].name);
+ env->SetObjectArrayElement(a, i, jname);
+ env->DeleteLocalRef(jname);
}
tr_metainfoFree(&info);
return a;
- CATCH:
+} CATCH:
return NULL;
}
// -------------------------------------------------------------------------------------------------
@@ -205,18 +207,19 @@ typedef struct ListFilesData {
static void *
torrentListFiles(tr_session *session, void *data, Err *err) {
+{
ListFilesData *d = (ListFilesData *) data;
tr_torrent *tor = findTorrentByIdEx(session, d->torrentId, err);
d->count = tor->info.fileCount;
if (d->count == 0) return NULL;
- d->fileNames = malloc(d->count * (sizeof(char *)));
+ d->fileNames = (char **) malloc(d->count * (sizeof(char *)));
for (int i = 0; i < d->count; i++) {
tr_file *f = &(tor->info.files[i]);
d->fileNames[i] = strdup(f->name);
}
- CATCH:
+} CATCH:
return NULL;
}
@@ -225,20 +228,21 @@ Java_com_ap_transmission_btc_Native_torrentListFiles(
JNIEnv *env, jclass __unused c, jlong jsession, jint torrentId) {
ListFilesData d = {.count=0};
jobjectArray result = NULL;
+{
d.torrentId = torrentId;
runInTransmissionThreadEx(env, jsession, torrentListFiles, &d);
if (d.count == 0) return NULL;
- result = (*env)->NewObjectArray(env, d.count, (*env)->FindClass(env, "java/lang/String"), NULL);
+ result = env->NewObjectArray(d.count, env->FindClass("java/lang/String"), NULL);
for (int i = 0; i < d.count; i++) {
- jobject jname = (*env)->NewStringUTF(env, d.fileNames[i]);
- (*env)->SetObjectArrayElement(env, result, i, jname);
- (*env)->DeleteLocalRef(env, jname);
+ jobject jname = env->NewStringUTF(d.fileNames[i]);
+ env->SetObjectArrayElement(result, i, jname);
+ env->DeleteLocalRef(jname);
free(d.fileNames[i]);
}
free(d.fileNames);
- CATCH:
+} CATCH:
return result;
}
// -------------------------------------------------------------------------------------------------
@@ -317,7 +321,7 @@ JNIEXPORT void JNICALL
Java_com_ap_transmission_btc_Native_torrentMagnetToTorrentFile(
JNIEnv *env, jclass __unused c, jlong jsession, jlong jsem,
jstring jmagnet, jstring jpath, jint timeout, jbooleanArray enqueue) {
- const char *magnet = (*env)->GetStringUTFChars(env, jmagnet, 0);
+ const char *magnet = env->GetStringUTFChars(jmagnet, 0);
sem_t *sem = (sem_t *) jsem;
MagnetToTorrentData d = {0};
d.sem = sem;
@@ -325,6 +329,7 @@ Java_com_ap_transmission_btc_Native_torrentMagnetToTorrentFile(
runInTransmissionThreadEx(env, jsession, torrentMagnetToTorrentFile, &d);
+{
int s = 0;
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
@@ -345,21 +350,21 @@ Java_com_ap_transmission_btc_Native_torrentMagnetToTorrentFile(
}
} else {
jboolean isCopy;
- jboolean *enq = (*env)->GetBooleanArrayElements(env, enqueue, &isCopy);
+ jboolean *enq = env->GetBooleanArrayElements(enqueue, &isCopy);
d.enqueue = enq[0];
- (*env)->ReleaseBooleanArrayElements(env, enqueue, enq, 0);
+ env->ReleaseBooleanArrayElements(enqueue, enq, 0);
if ((d.path != NULL) && !d.enqueue) {
- const char *path = (*env)->GetStringUTFChars(env, jpath, 0);
+ const char *path = env->GetStringUTFChars(jpath, 0);
cp(env, d.path, path);
- (*env)->ReleaseStringUTFChars(env, jpath, path);
+ env->ReleaseStringUTFChars(jpath, path);
} else if (!d.enqueue) {
throwEX(env, CLASS_IOEX, "Failed to download meta data");
}
}
- CATCH:
- (*env)->ReleaseStringUTFChars(env, jmagnet, magnet);
+} CATCH:
+ env->ReleaseStringUTFChars(jmagnet, magnet);
runInTransmissionThread(__FILENAME__, __LINE__, env, jsession,
torrentMagnetToTorrentFileCleanup, &d);
}
@@ -370,8 +375,8 @@ JNIEXPORT jint JNICALL
Java_com_ap_transmission_btc_Native_torrentFindByHash(
JNIEnv *env, jclass __unused c, jlong jsession, jbyteArray torrentHash) {
char hash[SHA_DIGEST_LENGTH];
- (*env)->GetByteArrayRegion(env, torrentHash, 0, sizeof(hash), (jbyte *) hash);
- return (jint) runInTransmissionThreadEx(env, jsession, findTorrentByHashFunc, hash);
+ env->GetByteArrayRegion(torrentHash, 0, sizeof(hash), (jbyte *) hash);
+ return (jint) (intptr_t) runInTransmissionThreadEx(env, jsession, findTorrentByHashFunc, hash);
CATCH:
return -1;
}
@@ -379,7 +384,7 @@ Java_com_ap_transmission_btc_Native_torrentFindByHash(
// ------------------------------------ torrentGetName ---------------------------------------------
static void *torrentGetName(tr_session *session, void *data, Err *err) {
- tr_torrent *tor = findTorrentByIdEx(session, (int) data, err);
+ tr_torrent *tor = findTorrentByIdEx(session, (int) (intptr_t) data, err);
return strdup(tor->info.name);
CATCH:
return NULL;
@@ -391,7 +396,7 @@ Java_com_ap_transmission_btc_Native_torrentGetName(
jstring jname = NULL;
char *name = (char *) runInTransmissionThreadEx(env, jsession, torrentGetName,
(void *) torrentId);
- jname = (*env)->NewStringUTF(env, name);
+ jname = env->NewStringUTF(name);
free(name);
CATCH:
return jname;
@@ -416,10 +421,10 @@ JNIEXPORT void JNICALL
Java_com_ap_transmission_btc_Native_torrentGetHash(
JNIEnv *env, jclass __unused c, jlong jsession, jint torrentId, jbyteArray torrentHash) {
GetHashData d = {torrentId};
- d.hash = (*env)->GetByteArrayElements(env, torrentHash, 0);
+ d.hash = env->GetByteArrayElements(torrentHash, 0);
runInTransmissionThreadEx(env, jsession, torrentGetHash, &d);
CATCH:
- (*env)->ReleaseByteArrayElements(env, torrentHash, d.hash, 0);
+ env->ReleaseByteArrayElements(torrentHash, d.hash, 0);
}
// -------------------------------------------------------------------------------------------------
@@ -431,6 +436,7 @@ typedef struct GetPieceHashData {
} GetPieceHashData;
static void *torrentGetPieceHash(tr_session *session, void *data, Err *err) {
+{
GetPieceHashData *d = (GetPieceHashData *) data;
tr_torrent *tor = findTorrentByIdEx(session, d->torrentId, err);
const tr_info *info = tr_torrentInfo(tor);
@@ -440,7 +446,7 @@ static void *torrentGetPieceHash(tr_session *session, void *data, Err *err) {
else
err->set(err, CLASS_IAEX, "Invalid piece index: %d", d->pieceIdx);
- CATCH:
+} CATCH:
return NULL;
}
@@ -449,10 +455,10 @@ Java_com_ap_transmission_btc_Native_torrentGetPieceHash(
JNIEnv *env, jclass __unused c, jlong jsession, jint torrentId,
jlong pieceIdx, jbyteArray pieceHash) {
GetPieceHashData d = {torrentId, pieceIdx};
- d.hash = (*env)->GetByteArrayElements(env, pieceHash, 0);
+ d.hash = env->GetByteArrayElements(pieceHash, 0);
runInTransmissionThreadEx(env, jsession, torrentGetPieceHash, &d);
CATCH:
- (*env)->ReleaseByteArrayElements(env, pieceHash, d.hash, 0);
+ env->ReleaseByteArrayElements(pieceHash, d.hash, 0);
}
// -------------------------------------------------------------------------------------------------
@@ -464,6 +470,7 @@ typedef struct SetPiecesHiPriData {
} SetPiecesHiPriData;
static void *torrentSetPiecesHiPri(tr_session *session, void *data, Err *err) {
+{
SetPiecesHiPriData *d = (SetPiecesHiPriData *) data;
tr_torrent *tor = findTorrentByIdEx(session, d->torrentId, err);
tr_piece *pieces = tor->info.pieces;
@@ -490,7 +497,7 @@ static void *torrentSetPiecesHiPri(tr_session *session, void *data, Err *err) {
else tr_torrentStart(tor);
}
- CATCH:
+} CATCH:
return NULL;
}
@@ -522,14 +529,15 @@ static void *torrentFindFile(tr_session *session, void *data, Err *err) {
JNIEXPORT jstring JNICALL
Java_com_ap_transmission_btc_Native_torrentFindFile(
JNIEnv *env, jclass __unused c, jlong jsession, jint torrentId, jint fileIdx) {
+{
FileData d = {torrentId, fileIdx};
char *path = (char *) runInTransmissionThreadEx(env, jsession, torrentFindFile, &d);
if (path == NULL) return NULL;
- jstring jpath = (*env)->NewStringUTF(env, path);
+ jstring jpath = env->NewStringUTF(path);
tr_free(path);
return jpath;
- CATCH:
+} CATCH:
return NULL;
}
// -------------------------------------------------------------------------------------------------
@@ -538,12 +546,13 @@ Java_com_ap_transmission_btc_Native_torrentFindFile(
static void *torrentGetFileName(tr_session *session, void *data, Err *err) {
FileData *d = (FileData *) data;
void *name = NULL;
+{
tr_torrent *tor = findTorrentByIdEx(session, d->torrentId, err);
const tr_file *f = getFileInfoEx(tor, (uint32_t) d->fileIdx, err);
size_t nameLen = strlen(f->name);
name = calloc(nameLen + 1, sizeof(char));
memcpy(name, f->name, nameLen);
- CATCH:
+} CATCH:
return name;
}
@@ -553,7 +562,7 @@ Java_com_ap_transmission_btc_Native_torrentGetFileName(
jstring jname = NULL;
FileData d = {torrentId, fileIdx};
char *name = (char *) runInTransmissionThreadEx(env, jsession, torrentGetFileName, &d);
- jname = (*env)->NewStringUTF(env, name);
+ jname = env->NewStringUTF(name);
free(name);
CATCH:
return jname;
@@ -570,8 +579,13 @@ typedef struct FileStatData {
static void *torrentGetFileStat(tr_session *session, void *data, Err *err) {
FileStatData *d = (FileStatData *) data;
- tr_torrent *tor = findTorrentByIdEx(session, d->torrentId, err);
- const tr_file *f = getFileInfoEx(tor, (uint32_t) d->fileIdx, err);
+ tr_torrent *tor = nullptr;
+ const tr_file *f = nullptr;
+
+ {
+ tor = findTorrentByIdEx(session, d->torrentId, err);
+ f = getFileInfoEx(tor, (uint32_t) d->fileIdx, err);
+ }
CATCH:
if ((tor == NULL) || (f == NULL)) return NULL;
@@ -583,7 +597,7 @@ static void *torrentGetFileStat(tr_session *session, void *data, Err *err) {
size_t fieldsCount = ((pieceCount % 64) == 0) ? (pieceCount / 64) : ((pieceCount / 64) + 1);
jsize statLen = d->bitFieldsLen = (jsize) (fieldsCount + 6);
jlong *bitFields = d->bitFields;
- if (bitFields == NULL) bitFields = d->bitFields = malloc(sizeof(jlong) * statLen);
+ if (bitFields == NULL) bitFields = d->bitFields = (jlong *) malloc(sizeof(jlong) * statLen);
tr_torrentAvailability(tor, tabs, info->pieceCount);
@@ -617,19 +631,19 @@ static void *torrentGetFileStat(tr_session *session, void *data, Err *err) {
* jlongArray[5] = fileComplete
* jlongArray[6...] = pieceBitFields
*/
-JNIEXPORT jbyteArray JNICALL
+JNIEXPORT jlongArray JNICALL
Java_com_ap_transmission_btc_Native_torrentGetFileStat(
- JNIEnv *env, jclass __unused c, jlong jsession, jint torrentId, jint fileIdx, jbyteArray stat) {
+ JNIEnv *env, jclass __unused c, jlong jsession, jint torrentId, jint fileIdx, jlongArray stat) {
FileStatData d = {.torrentId= torrentId, .fileIdx = fileIdx, .bitFields = NULL};
- if (stat != NULL) d.bitFields = (*env)->GetLongArrayElements(env, stat, 0);
+ if (stat != NULL) d.bitFields = env->GetLongArrayElements(stat, 0);
runInTransmissionThreadEx(env, jsession, torrentGetFileStat, &d);
if (stat == NULL) {
- stat = (*env)->NewLongArray(env, d.bitFieldsLen);
- (*env)->SetLongArrayRegion(env, stat, 0, d.bitFieldsLen, d.bitFields);
+ stat = env->NewLongArray(d.bitFieldsLen);
+ env->SetLongArrayRegion(stat, 0, d.bitFieldsLen, d.bitFields);
free(d.bitFields);
} else {
- (*env)->ReleaseLongArrayElements(env, stat, d.bitFields, 0);
+ env->ReleaseLongArrayElements(stat, d.bitFields, 0);
}
CATCH:
@@ -675,10 +689,10 @@ JNIEXPORT void JNICALL
Java_com_ap_transmission_btc_Native_torrentGetPiece(
JNIEnv *env, jclass __unused c, jlong jsession, jint torrentId, jlong pieceIdx,
jbyteArray dst, jint offset, jint len) {
- GetPieceData d = {torrentId, pieceIdx, (*env)->GetByteArrayElements(env, dst, 0), offset, len};
+ GetPieceData d = {torrentId, pieceIdx, env->GetByteArrayElements(dst, 0), offset, len};
runInTransmissionThreadEx(env, jsession, torrentGetPiece, &d);
CATCH:
- (*env)->ReleaseByteArrayElements(env, dst, d.dst, 0);
+ env->ReleaseByteArrayElements(dst, d.dst, 0);
}
// -------------------------------------------------------------------------------------------------
@@ -716,44 +730,45 @@ typedef struct StatBriefData {
*/
static void *torrentStatBrief(tr_session *session, void *data, __unused Err *err) {
StatBriefData *d = (StatBriefData *) data;
- tr_torrent *it = session->torrentList;
- int numTorrents = session->torrentCount;
+ int numTorrents = session->torrents.size();
int statLen = numTorrents * 10;
if ((d->stat == NULL) || (statLen != d->statLen)) {
- d->stat = malloc(sizeof(jlong) * statLen);
+ d->stat = (jlong *) malloc(sizeof(jlong) * statLen);
d->statLen = statLen;
d->alloc = true;
}
- for (int i = 0; (it != NULL); it = it->next, i += 10) {
- tr_torrent_activity status = tr_torrentGetActivity(it);
+ int i = -10;
+ for (auto tor : session->torrents) {
+ i += 10;
+ tr_torrent_activity status = tr_torrentGetActivity(tor);
- d->stat[i] = it->uniqueId;
- d->stat[i + 3] = tr_cpSizeWhenDone(&it->completion);
- d->stat[i + 4] = tr_torrentGetLeftUntilDone(it);
- d->stat[i + 5] = it->uploadedCur + it->uploadedPrev;
+ d->stat[i] = tor->uniqueId;
+ d->stat[i + 3] = tr_cpSizeWhenDone(&tor->completion);
+ d->stat[i + 4] = tr_torrentGetLeftUntilDone(tor);
+ d->stat[i + 5] = tor->uploadedCur + tor->uploadedPrev;
- if (it->error != TR_STAT_LOCAL_ERROR) {
+ if (tor->error != TR_STAT_LOCAL_ERROR) {
switch (status) {
case TR_STATUS_STOPPED:
d->stat[i + 1] = 0;
- d->stat[i + 2] = (jlong) (tr_cpPercentDone(&it->completion) * 100);
+ d->stat[i + 2] = (jlong) (tr_cpPercentDone(&tor->completion) * 100);
continue;
case TR_STATUS_CHECK_WAIT :
case TR_STATUS_CHECK:
d->stat[i + 1] = 1;
- d->stat[i + 2] = (jlong) (getVerifyProgress(it) * 100);
+ d->stat[i + 2] = (jlong) (getVerifyProgress(tor) * 100);
break;
case TR_STATUS_DOWNLOAD_WAIT:
case TR_STATUS_DOWNLOAD :
d->stat[i + 1] = 2;
- d->stat[i + 2] = (jlong) (tr_cpPercentDone(&it->completion) * 100);
+ d->stat[i + 2] = (jlong) (tr_cpPercentDone(&tor->completion) * 100);
break;
case TR_STATUS_SEED_WAIT :
case TR_STATUS_SEED:
d->stat[i + 1] = 3;
- d->stat[i + 2] = (jlong) (tr_cpPercentDone(&it->completion) * 100);
+ d->stat[i + 2] = (jlong) (tr_cpPercentDone(&tor->completion) * 100);
break;
}
} else {
@@ -761,14 +776,14 @@ static void *torrentStatBrief(tr_session *session, void *data, __unused Err *err
d->stat[i + 2] = 0;
}
- if (it->swarm != NULL) {
+ if (tor->swarm != NULL) {
uint64_t const now = tr_time_msec();
struct tr_swarm_stats sstat;
- tr_swarmGetStats(it->swarm, &sstat);
+ tr_swarmGetStats(tor->swarm, &sstat);
d->stat[i + 6] = sstat.activePeerCount[TR_UP];
d->stat[i + 7] = sstat.activePeerCount[TR_DOWN] + sstat.activeWebseedCount;
- d->stat[i + 8] = tr_bandwidthGetPieceSpeed_Bps(&it->bandwidth, now, TR_UP);
- d->stat[i + 9] = tr_bandwidthGetPieceSpeed_Bps(&it->bandwidth, now, TR_DOWN);
+ d->stat[i + 8] = tor->bandwidth->getPieceSpeedBytesPerSecond(now, TR_UP);
+ d->stat[i + 9] = tor->bandwidth->getPieceSpeedBytesPerSecond(now, TR_DOWN);
} else {
d->stat[i + 6] = 0L;
d->stat[i + 7] = 0L;
@@ -788,8 +803,8 @@ Java_com_ap_transmission_btc_Native_torrentStatBrief(
bool release = false;
if (jstat != NULL) {
- d.stat = stat = (*env)->GetLongArrayElements(env, jstat, 0);
- d.statLen = (*env)->GetArrayLength(env, jstat);
+ d.stat = stat = env->GetLongArrayElements(jstat, 0);
+ d.statLen = env->GetArrayLength(jstat);
release = true;
}
@@ -797,11 +812,11 @@ Java_com_ap_transmission_btc_Native_torrentStatBrief(
CATCH:
if (release) {
- (*env)->ReleaseLongArrayElements(env, jstat, stat, 0);
+ env->ReleaseLongArrayElements(jstat, stat, 0);
}
if (d.alloc) {
- jstat = (*env)->NewLongArray(env, d.statLen);
- (*env)->SetLongArrayRegion(env, jstat, 0, d.statLen, d.stat);
+ jstat = env->NewLongArray(d.statLen);
+ env->SetLongArrayRegion(jstat, 0, d.statLen, d.stat);
free(d.stat);
}
@@ -811,7 +826,7 @@ Java_com_ap_transmission_btc_Native_torrentStatBrief(
// ------------------------------------- torrentGetError -------------------------------------------
static void *torrentGetError(tr_session *session, void *data, Err *err) {
- tr_torrent *tor = findTorrentByIdEx(session, (int) data, err);
+ tr_torrent *tor = findTorrentByIdEx(session, (int) (intptr_t) data, err);
return strdup(tor->errorString);
CATCH:
return NULL;
@@ -820,12 +835,13 @@ static void *torrentGetError(tr_session *session, void *data, Err *err) {
JNIEXPORT jstring JNICALL
Java_com_ap_transmission_btc_Native_torrentGetError(
JNIEnv *env, jclass __unused c, jlong jsession, jint torrentId) {
+{
char *err = (char *) runInTransmissionThreadEx(env, jsession, torrentGetError,
(void *) torrentId);
- jstring jerr = (*env)->NewStringUTF(env, err);
+ jstring jerr = env->NewStringUTF(err);
free(err);
return jerr;
- CATCH:
+} CATCH:
return NULL;
}
// -------------------------------------------------------------------------------------------------
@@ -852,11 +868,11 @@ Java_com_ap_transmission_btc_Native_torrentSetDnd(
SetDndData d;
d.dnd = dnd;
d.torrentId = torrentId;
- d.files = (tr_file_index_t *) (*env)->GetIntArrayElements(env, files, 0);
- d.fileCount = (tr_file_index_t) (*env)->GetArrayLength(env, files);
+ d.files = (tr_file_index_t *) env->GetIntArrayElements(files, 0);
+ d.fileCount = (tr_file_index_t) env->GetArrayLength(files);
runInTransmissionThreadEx(env, jsession, torrentSetDnd, &d);
CATCH:
- (*env)->ReleaseIntArrayElements(env, files, (jint *) d.files, 0);
+ env->ReleaseIntArrayElements(files, (jint *) d.files, 0);
}
// -------------------------------------------------------------------------------------------------
@@ -878,10 +894,11 @@ JNIEXPORT void JNICALL
Java_com_ap_transmission_btc_Native_torrentSetLocation(
JNIEnv *env, jclass __unused c, jlong jsession, jint torrentId, jstring jpath) {
jboolean isCopy;
- const char *path = (*env)->GetStringUTFChars(env, jpath, &isCopy);
+ const char *path = env->GetStringUTFChars(jpath, &isCopy);
SetLocationData d = {torrentId, path};
runInTransmissionThreadEx(env, jsession, torrentSetLocation, &d);
CATCH:
- (*env)->ReleaseStringUTFChars(env, jpath, path);
+ env->ReleaseStringUTFChars(jpath, path);
}
// -------------------------------------------------------------------------------------------------
+} // extern "C"
\ No newline at end of file
diff --git a/src/main/cpp/transmission.c b/src/main/cpp/transmission.cc
similarity index 81%
rename from src/main/cpp/transmission.c
rename to src/main/cpp/transmission.cc
index ec45604..108bbcf 100644
--- a/src/main/cpp/transmission.c
+++ b/src/main/cpp/transmission.cc
@@ -23,9 +23,11 @@
#define SPEED_G_STR "GB/s"
#define SPEED_T_STR "TB/s"
+extern "C" {
+
JNIEXPORT jstring JNICALL
Java_com_ap_transmission_btc_Native_transmissionVersion(JNIEnv *env, jclass __unused c) {
- return (*env)->NewStringUTF(env, SHORT_VERSION_STRING);
+ return env->NewStringUTF(SHORT_VERSION_STRING);
}
// ----------------------------------------- Start -------------------------------------------------
@@ -51,11 +53,11 @@ static tr_rpc_callback_status rpcFunc(tr_session *__unused session, tr_rpc_callb
switch (type) {
case TR_RPC_TORRENT_ADDED:
if ((tor != NULL) && !tr_torrentHasMetadata(tor)) {
- tr_torrent_metadata_func mdFunc = tor->metadata_func_user_data;
+ tr_torrent_metadata_func mdFunc = (tr_torrent_metadata_func) tor->metadata_func_user_data;
OrigMetaDataFunc *origData = NULL;
if (mdFunc != NULL) {
- origData = malloc(sizeof(OrigMetaDataFunc));
+ origData = (OrigMetaDataFunc *) malloc(sizeof(OrigMetaDataFunc));
origData->metadata_func = mdFunc;
origData->metadata_func_user_data = tor->metadata_func_user_data;
}
@@ -88,7 +90,7 @@ static void altSpeedFunc(tr_session *__unused session, bool __unused active, boo
}
static void *transmissionStart(tr_session *session, void *data, Err *__unused err) {
- jboolean suspend = (jboolean) data;
+ jboolean suspend = (jboolean) (intptr_t) data;
tr_sessionSetPaused(session, false);
tr_sessionSetRPCCallback(session, rpcFunc, NULL);
tr_sessionSetAltSpeedFunc(session, altSpeedFunc, NULL);
@@ -114,7 +116,7 @@ Java_com_ap_transmission_btc_Native_transmissionStart(
jboolean loadConfig, jboolean enableSequential, jboolean suspend) {
tr_variant settings;
tr_session *session;
- const char *configDir = (*env)->GetStringUTFChars(env, jconfigDir, 0);
+ const char *configDir = env->GetStringUTFChars(jconfigDir, 0);
tr_variantInitDict(&settings, 0);
if (loadConfig) {
@@ -130,9 +132,9 @@ Java_com_ap_transmission_btc_Native_transmissionStart(
tr_variantDictAddBool(&settings, TR_KEY_peer_port_random_on_start, true);
}
- const char *downloadsDir = (*env)->GetStringUTFChars(env, jdownloadsDir, 0);
+ const char *downloadsDir = env->GetStringUTFChars(jdownloadsDir, 0);
tr_variantDictAddStr(&settings, TR_KEY_download_dir, downloadsDir);
- (*env)->ReleaseStringUTFChars(env, jdownloadsDir, downloadsDir);
+ env->ReleaseStringUTFChars(jdownloadsDir, downloadsDir);
tr_variantDictAddInt(&settings, TR_KEY_encryption, encrMode);
tr_variantDictAddBool(&settings, TR_KEY_sequentialDownload, enableSequential);
@@ -142,21 +144,21 @@ Java_com_ap_transmission_btc_Native_transmissionStart(
tr_variantDictAddInt(&settings, TR_KEY_rpc_port, rpcPort);
if (enableAuth) {
- const char *username = (*env)->GetStringUTFChars(env, jusername, 0);
- const char *password = (*env)->GetStringUTFChars(env, jpassword, 0);
+ const char *username = env->GetStringUTFChars(jusername, 0);
+ const char *password = env->GetStringUTFChars(jpassword, 0);
tr_variantDictAddStr(&settings, TR_KEY_rpc_username, username);
tr_variantDictAddStr(&settings, TR_KEY_rpc_password, password);
- (*env)->ReleaseStringUTFChars(env, jusername, username);
- (*env)->ReleaseStringUTFChars(env, jpassword, password);
+ env->ReleaseStringUTFChars(jusername, username);
+ env->ReleaseStringUTFChars(jpassword, password);
tr_variantDictAddBool(&settings, TR_KEY_rpc_authentication_required, true);
} else {
tr_variantDictAddBool(&settings, TR_KEY_rpc_authentication_required, false);
}
if (enableRpcWhitelist) {
- const char *rpcWhitelist = (*env)->GetStringUTFChars(env, jrpcWhitelist, 0);
+ const char *rpcWhitelist = env->GetStringUTFChars(jrpcWhitelist, 0);
tr_variantDictAddStr(&settings, TR_KEY_rpc_whitelist, rpcWhitelist);
- (*env)->ReleaseStringUTFChars(env, jrpcWhitelist, rpcWhitelist);
+ env->ReleaseStringUTFChars(jrpcWhitelist, rpcWhitelist);
tr_variantDictAddBool(&settings, TR_KEY_rpc_whitelist_enabled, true);
} else {
tr_variantDictAddBool(&settings, TR_KEY_rpc_whitelist_enabled, false);
@@ -170,11 +172,11 @@ Java_com_ap_transmission_btc_Native_transmissionStart(
tr_formatter_speed_init(SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR);
session = tr_sessionInit(configDir, true, &settings);
tr_sessionSaveSettings(session, configDir, &settings);
- (*env)->ReleaseStringUTFChars(env, jconfigDir, configDir);
+ env->ReleaseStringUTFChars(jconfigDir, configDir);
tr_variantFree(&settings);
jlong jsession = (jlong) session;
- runInTransmissionThreadEx(env, jsession, transmissionStart, (void *) suspend);
+ runInTransmissionThreadEx(env, jsession, transmissionStart, (void *) (intptr_t) suspend);
return jsession;
CATCH:
@@ -193,9 +195,9 @@ Java_com_ap_transmission_btc_Native_transmissionStop(
tr_variantInitDict(&settings, 0);
tr_sessionGetSettings(session, &settings);
- const char *configDir = (*env)->GetStringUTFChars(env, jconfigDir, 0);
+ const char *configDir = env->GetStringUTFChars(jconfigDir, 0);
tr_sessionSaveSettings(session, configDir, &settings);
- (*env)->ReleaseStringUTFChars(env, jconfigDir, configDir);
+ env->ReleaseStringUTFChars(jconfigDir, configDir);
tr_variantFree(&settings);
tr_sessionClose(session);
@@ -211,7 +213,7 @@ static void *transmissionSuspend(tr_session *session, void *data, Err *__unused
JNIEXPORT void JNICALL
Java_com_ap_transmission_btc_Native_transmissionSuspend(
JNIEnv *__unused env, jclass __unused c, jlong jsession, jboolean suspend) {
- runInTransmissionThreadEx(env, jsession, transmissionSuspend, (void *) suspend);
+ runInTransmissionThreadEx(env, jsession, transmissionSuspend, (void *) (intptr_t) suspend);
CATCH:;
}
// -------------------------------------------------------------------------------------------------
@@ -219,8 +221,8 @@ Java_com_ap_transmission_btc_Native_transmissionSuspend(
// -------------------------------- HasDownloadingTorrents -----------------------------------------
static void *
transmissionHasDownloadingTorrents(tr_session *session, void *__unused data, Err *__unused err) {
- for (tr_torrent *it = session->torrentList; it != NULL; it = it->next) {
- switch (tr_torrentGetActivity(it)) {
+ for (auto tor : session->torrents) {
+ switch (tr_torrentGetActivity(tor)) {
case TR_STATUS_DOWNLOAD:
case TR_STATUS_DOWNLOAD_WAIT:
case TR_STATUS_CHECK:
@@ -237,8 +239,8 @@ transmissionHasDownloadingTorrents(tr_session *session, void *__unused data, Err
JNIEXPORT jboolean JNICALL
Java_com_ap_transmission_btc_Native_transmissionHasDownloadingTorrents(
JNIEnv *__unused env, jclass __unused c, jlong jsession) {
- return (jboolean) runInTransmissionThreadEx(env, jsession, transmissionHasDownloadingTorrents,
- NULL);
+ return (jboolean) (intptr_t) runInTransmissionThreadEx(env, jsession,
+ transmissionHasDownloadingTorrents, NULL);
CATCH:
return JNI_FALSE;
}
@@ -253,17 +255,17 @@ typedef struct ListTorrentsData {
static void *
transmissionListTorrentNames(tr_session *session, void *data, Err *__unused err) {
ListTorrentsData *d = (ListTorrentsData *) data;
- d->count = session->torrentCount;
+ d->count = session->torrents.size();
if (d->count == 0) return NULL;
- d->torrents = malloc(d->count * (sizeof(char *)));
+ d->torrents = (char **) malloc(d->count * (sizeof(char *)));
int i = 0;
- for (tr_torrent *it = session->torrentList; it != NULL; it = it->next) {
- size_t hashLen = sizeof(it->info.hashString);
- size_t nameLen = strlen(it->info.name);
+ for (auto tor : session->torrents) {
+ size_t hashLen = sizeof(tor->info.hashString);
+ size_t nameLen = strlen(tor->info.name);
size_t lineLen = hashLen + nameLen + 12;
- char *line = malloc(lineLen);
- snprintf(line, lineLen, "%d %s %s", tr_torrentId(it), it->info.hashString, it->info.name);
+ char *line = (char *) malloc(lineLen);
+ snprintf(line, lineLen, "%d %s %s", tr_torrentId(tor), tor->info.hashString, tor->info.name);
d->torrents[i++] = line;
}
@@ -278,12 +280,12 @@ Java_com_ap_transmission_btc_Native_transmissionListTorrentNames(
runInTransmissionThreadEx(env, jsession, transmissionListTorrentNames, &d);
if (d.count == 0) return NULL;
- result = (*env)->NewObjectArray(env, d.count, (*env)->FindClass(env, "java/lang/String"), NULL);
+ result = env->NewObjectArray(d.count, env->FindClass("java/lang/String"), NULL);
for (int i = 0; i < d.count; i++) {
- jobject jname = (*env)->NewStringUTF(env, d.torrents[i]);
- (*env)->SetObjectArrayElement(env, result, i, jname);
- (*env)->DeleteLocalRef(env, jname);
+ jobject jname = env->NewStringUTF(d.torrents[i]);
+ env->SetObjectArrayElement(result, i, jname);
+ env->DeleteLocalRef(jname);
free(d.torrents[i]);
}
@@ -299,4 +301,6 @@ Java_com_ap_transmission_btc_Native_transmissionGetEncryptionMode(
JNIEnv *__unused env, jclass __unused c, jlong jsession) {
return tr_sessionGetEncryption((tr_session *) jsession);
}
-// -------------------------------------------------------------------------------------------------
\ No newline at end of file
+// -------------------------------------------------------------------------------------------------
+
+} //extern "C"
\ No newline at end of file
diff --git a/src/main/java/com/ap/transmission/btc/Utils.java b/src/main/java/com/ap/transmission/btc/Utils.java
index d50f14e..4a24a09 100644
--- a/src/main/java/com/ap/transmission/btc/Utils.java
+++ b/src/main/java/com/ap/transmission/btc/Utils.java
@@ -1,5 +1,12 @@
package com.ap.transmission.btc;
+import static android.content.Context.CONNECTIVITY_SERVICE;
+import static android.content.Context.WIFI_SERVICE;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static com.ap.transmission.btc.BuildConfig.BUILD_TYPE;
+import static java.net.NetworkInterface.getNetworkInterfaces;
+
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
@@ -7,6 +14,7 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.ConnectivityManager;
@@ -76,13 +84,6 @@
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
-import static android.content.Context.CONNECTIVITY_SERVICE;
-import static android.content.Context.WIFI_SERVICE;
-import static android.net.ConnectivityManager.TYPE_ETHERNET;
-import static android.net.ConnectivityManager.TYPE_WIFI;
-import static com.ap.transmission.btc.BuildConfig.BUILD_TYPE;
-import static java.net.NetworkInterface.getNetworkInterfaces;
-
/**
* @author Andrey Pavlenko
*/
@@ -846,4 +847,15 @@ public static void openUri(Activity a, Uri uri, String mime) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
a.startActivity(Intent.createChooser(intent, a.getResources().getString(R.string.open_with)));
}
+
+ public static boolean hasManifestPermission(Context ctx, String perm) {
+ try {
+ String[] perms = ctx.getPackageManager().
+ getPackageInfo(ctx.getPackageName(), PackageManager.GET_PERMISSIONS)
+ .requestedPermissions;
+ return Arrays.asList(perms).contains(perm);
+ } catch (PackageManager.NameNotFoundException ignore) {
+ return false;
+ }
+ }
}
diff --git a/src/main/java/com/ap/transmission/btc/activities/SelectFileActivity.java b/src/main/java/com/ap/transmission/btc/activities/SelectFileActivity.java
index 6f07bbb..ac30633 100644
--- a/src/main/java/com/ap/transmission/btc/activities/SelectFileActivity.java
+++ b/src/main/java/com/ap/transmission/btc/activities/SelectFileActivity.java
@@ -12,6 +12,7 @@
import static com.ap.transmission.btc.Utils.showErr;
import static com.ap.transmission.btc.Utils.showMsg;
+import android.Manifest;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.ListActivity;
@@ -112,7 +113,9 @@ protected void onCreate(Bundle savedInstanceState) {
}
}
- if ((SDK_INT >= VERSION_CODES.R) && !Environment.isExternalStorageManager()) {
+ if ((SDK_INT >= VERSION_CODES.R)
+ && Utils.hasManifestPermission(this, Manifest.permission.MANAGE_EXTERNAL_STORAGE)
+ && !Environment.isExternalStorageManager()) {
Intent req = new Intent(ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
Uri uri = Uri.fromParts("package", getPackageName(), null);
req.setData(uri);
diff --git a/src/main/java/com/ap/transmission/btc/torrent/Transmission.java b/src/main/java/com/ap/transmission/btc/torrent/Transmission.java
index e26b679..1981eba 100644
--- a/src/main/java/com/ap/transmission/btc/torrent/Transmission.java
+++ b/src/main/java/com/ap/transmission/btc/torrent/Transmission.java
@@ -55,627 +55,631 @@
* @author Andrey Pavlenko
*/
public class Transmission {
- private static final byte STATE_STOPPED = 0;
- private static final byte STATE_STARTING = -1;
- private static final byte STATE_STOPPING = -2;
- private static final String TAG = Transmission.class.getName();
- private static final String SETTINGS_FILE = "settings.json";
- private final ReadWriteLock lock = new ReentrantReadWriteLock();
- private final Prefs prefs;
- private List watchers;
- private PowerLock powerLock;
- private SsdpServer ssdpServer;
- private volatile List semaphores;
- private volatile HttpServer httpServer;
- private volatile TorrentFs torrentFs;
- private volatile ExecutorService executor;
- private volatile ScheduledExecutorService scheduler;
- private volatile long session = STATE_STOPPED;
- private volatile byte suspended;
-
- public Transmission(Prefs prefs) {
- this.prefs = prefs;
- }
-
- public static String getVersion() {
- return Native.transmissionVersion();
- }
-
- public Prefs getPrefs() {
- return prefs;
- }
-
- public Context getContext() {
- return getPrefs().getContext();
- }
-
- public Lock readLock() {
- return lock.readLock();
- }
-
- public Lock writeLock() {
- return lock.writeLock();
- }
-
- long getSession() {
- return session;
- }
-
- public TorrentFs getTorrentFs() {
- TorrentFs fs = torrentFs;
-
- if (fs == null) {
- throw new IllegalStateException("Transmission is not running");
- }
-
- return fs;
- }
-
- public HttpServer getHttpServer() throws IllegalStateException, IOException {
- HttpServer s = httpServer;
-
- if (s == null) {
- writeLock().lock();
- try {
- if ((s = httpServer) == null) {
- checkRunning();
- httpServer = s = new SimpleHttpServer(this);
- s.start();
- }
- } finally {
- writeLock().unlock();
- }
- }
-
- return s;
- }
-
- public void start() throws IOException {
- if (isRunning()) return;
-
- writeLock().lock();
- if (isRunning()) return;
- session = STATE_STARTING;
- boolean ok = false;
- getExecutor(); // Start executor
-
- try {
- Context ctx = getContext();
- boolean suspend = false;
- File dataDir = new File(ctx.getApplicationInfo().dataDir);
- File configDir = new File(prefs.getSettingsDir());
- File downloadDir = new File(prefs.getDownloadDir());
- File webDir = new File(dataDir, "web");
- File settings = new File(configDir, SETTINGS_FILE);
- File tmp = new File(dataDir, "tmp");
- File indexHtml = new File(dataDir, "web/index.html");
- File indexOrigHtml = prefs.isAltWebEnabled() ? new File(dataDir, "web/index.webcontrol.html")
- : new File(dataDir, "web/index.original.html");
- mkdirs(configDir, downloadDir, tmp);
-
- copyAssets(ctx.getAssets(), "web", dataDir, true);
- if (indexHtml.length() != indexOrigHtml.length()) Utils.transfer(indexOrigHtml, indexHtml);
-
- if (prefs.isWifiEthOnly()) {
- suspend = !Utils.isWifiEthActive(ctx, prefs.getWifiSsid());
- ConnectivityChangeReceiver.register(ctx);
- }
-
- Native.envSet("TMP", tmp.getAbsolutePath());
- Native.envSet("TRANSMISSION_WEB_HOME", webDir.getAbsolutePath());
- increaseSoBuf();
- configureProxy(prefs);
-
- session = Native.transmissionStart(configDir.getAbsolutePath(),
- downloadDir.getAbsolutePath(), prefs.getEncryptionMode().ordinal(),
- prefs.isRpcEnabled(), prefs.getRpcPort(),
- prefs.isRpcAuthEnabled(), prefs.getRpcUsername(), prefs.getRpcPassword(),
- prefs.isRpcWhitelistEnabled(), prefs.getRpcWhitelist(), settings.exists(),
- prefs.isSeqDownloadEnabled(), suspend);
- suspended = (byte) (suspend ? 1 : 0);
- debug(TAG, "Session created: %d", session);
- torrentFs = new TorrentFs(this, session);
-
- startWatchers();
- startUpnp();
- if (hasDownloadingTorrents()) wakeLock();
-
- // Handle callbacks in a separate thread to avoid dead locks
- Native.transmissionSetRpcCallbacks(
- () -> getExecutor().submit(this::torrentAddedOrChanged),
- () -> getExecutor().submit(this::torrentRemoved),
- () -> getExecutor().submit(this::sessionChanged),
- () -> getExecutor().submit(this::cheduledAltSpeed));
-
- ok = true;
- } finally {
- writeLock().unlock();
- if (!ok) stop();
- }
- }
-
- private void startWatchers() {
- if (prefs.isWatchDirEnabled()) {
- int interval = prefs.getWatchInterval();
- watchers = new ArrayList<>();
-
- for (Map.Entry e : prefs.getWatchDirs().entrySet()) {
- startWatcher(e.getKey(), e.getValue());
- }
-
- if (interval > 0) {
+ private static final byte STATE_STOPPED = 0;
+ private static final byte STATE_STARTING = 1;
+ private static final byte STATE_RUNNING = 2;
+ private static final byte STATE_STOPPING = 4;
+ private static final byte STATE_SUSPENDED = 8;
+ private static final byte STATE_SUSPENDED_BY_USER = 16;
+ private static final String TAG = Transmission.class.getName();
+ private static final String SETTINGS_FILE = "settings.json";
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+ private final Prefs prefs;
+ private List watchers;
+ private PowerLock powerLock;
+ private SsdpServer ssdpServer;
+ private volatile List semaphores;
+ private volatile HttpServer httpServer;
+ private volatile TorrentFs torrentFs;
+ private volatile ExecutorService executor;
+ private volatile ScheduledExecutorService scheduler;
+ private volatile long session;
+ private volatile byte state = STATE_STOPPED;
+
+ public Transmission(Prefs prefs) {
+ this.prefs = prefs;
+ }
+
+ public static String getVersion() {
+ return Native.transmissionVersion();
+ }
+
+ public Prefs getPrefs() {
+ return prefs;
+ }
+
+ public Context getContext() {
+ return getPrefs().getContext();
+ }
+
+ public Lock readLock() {
+ return lock.readLock();
+ }
+
+ public Lock writeLock() {
+ return lock.writeLock();
+ }
+
+ long getSession() {
+ return session;
+ }
+
+ public TorrentFs getTorrentFs() {
+ TorrentFs fs = torrentFs;
+
+ if (fs == null) {
+ throw new IllegalStateException("Transmission is not running");
+ }
+
+ return fs;
+ }
+
+ public HttpServer getHttpServer() throws IllegalStateException, IOException {
+ HttpServer s = httpServer;
+
+ if (s == null) {
+ writeLock().lock();
+ try {
+ if ((s = httpServer) == null) {
+ checkRunning();
+ httpServer = s = new SimpleHttpServer(this);
+ s.start();
+ }
+ } finally {
+ writeLock().unlock();
+ }
+ }
+
+ return s;
+ }
+
+ public void start() throws IOException {
+ if (isRunning()) return;
+
+ writeLock().lock();
+ if (isRunning()) return;
+ state = STATE_STARTING;
+ boolean ok = false;
+ getExecutor(); // Start executor
+
+ try {
+ Context ctx = getContext();
+ boolean suspend = false;
+ File dataDir = new File(ctx.getApplicationInfo().dataDir);
+ File configDir = new File(prefs.getSettingsDir());
+ File downloadDir = new File(prefs.getDownloadDir());
+ File webDir = new File(dataDir, "web");
+ File settings = new File(configDir, SETTINGS_FILE);
+ File tmp = new File(dataDir, "tmp");
+ File indexHtml = new File(dataDir, "web/index.html");
+ File indexOrigHtml = prefs.isAltWebEnabled() ? new File(dataDir, "web/index.webcontrol.html")
+ : new File(dataDir, "web/index.original.html");
+ mkdirs(configDir, downloadDir, tmp);
+
+ copyAssets(ctx.getAssets(), "web", dataDir, true);
+ if (indexHtml.length() != indexOrigHtml.length()) Utils.transfer(indexOrigHtml, indexHtml);
+
+ if (prefs.isWifiEthOnly()) {
+ suspend = !Utils.isWifiEthActive(ctx, prefs.getWifiSsid());
+ ConnectivityChangeReceiver.register(ctx);
+ }
+
+ Native.envSet("TMP", tmp.getAbsolutePath());
+ Native.envSet("TRANSMISSION_WEB_HOME", webDir.getAbsolutePath());
+ increaseSoBuf();
+ configureProxy(prefs);
+
+ session = Native.transmissionStart(configDir.getAbsolutePath(),
+ downloadDir.getAbsolutePath(), prefs.getEncryptionMode().ordinal(),
+ prefs.isRpcEnabled(), prefs.getRpcPort(),
+ prefs.isRpcAuthEnabled(), prefs.getRpcUsername(), prefs.getRpcPassword(),
+ prefs.isRpcWhitelistEnabled(), prefs.getRpcWhitelist(), settings.exists(),
+ prefs.isSeqDownloadEnabled(), suspend);
+ state = suspend ? (STATE_RUNNING | STATE_SUSPENDED) : STATE_RUNNING;
+ debug(TAG, "Session created: %d", session);
+ torrentFs = new TorrentFs(this, session);
+
+ startWatchers();
+ startUpnp();
+ if (hasDownloadingTorrents()) wakeLock();
+
+ // Handle callbacks in a separate thread to avoid dead locks
+ Native.transmissionSetRpcCallbacks(
+ () -> getExecutor().submit(this::torrentAddedOrChanged),
+ () -> getExecutor().submit(this::torrentRemoved),
+ () -> getExecutor().submit(this::sessionChanged),
+ () -> getExecutor().submit(this::cheduledAltSpeed));
+
+ ok = true;
+ } finally {
+ writeLock().unlock();
+ if (!ok) stop();
+ }
+ }
+
+ private void startWatchers() {
+ if (prefs.isWatchDirEnabled()) {
+ int interval = prefs.getWatchInterval();
+ watchers = new ArrayList<>();
+
+ for (Map.Entry e : prefs.getWatchDirs().entrySet()) {
+ startWatcher(e.getKey(), e.getValue());
+ }
+
+ if (interval > 0) {
ScheduledExecutorService sched = getScheduler();
sched.scheduleWithFixedDelay(() -> {
if (!isRunning() || (watchers == null)) return;
for (Watcher w : watchers) w.scan();
}, interval, interval, TimeUnit.SECONDS);
}
- }
- }
-
- private void startWatcher(String watchDir, String downloadDir) {
- File wd = new File(watchDir);
- mkdirs(wd);
- Watcher w = new Watcher(wd, new File(downloadDir));
- w.startWatching();
- watchers.add(w);
- }
-
- private void startUpnp() {
- if (!prefs.isUpnpEnabled()) return;
- if ((ssdpServer != null) && ssdpServer.isRunning()) return;
- HttpServer httpServer;
-
- try {
- httpServer = getHttpServer();
- } catch (IOException ex) {
- err(TAG, ex, "Failed to start HTTP Server");
- return;
- }
-
- try {
- if (ssdpServer == null) ssdpServer = new SsdpServer(httpServer);
- ssdpServer.start();
- } catch (IOException ex) {
- err(TAG, ex, "Failed to start SSDP server, SSDP NOTIFY will be sent every 60 seconds");
- }
- }
-
- public void stop() {
- long s = session;
- if (s <= 0) return;
-
- writeLock().lock();
- try {
- if ((s = session) <= 0) return;
- session = STATE_STOPPING;
-
- try {
- if (semaphores != null) for (Long sem : semaphores) Native.semPost(sem);
- if (watchers != null) for (Watcher w : watchers) w.stopWatching();
- ConnectivityChangeReceiver.unregister(getContext());
- Native.transmissionSetRpcCallbacks(null, null, null, null);
- Utils.close(httpServer, ssdpServer);
- stopExecutor();
- stopSheduler();
-
- debug(TAG, "Closing session: %d", s);
- File configDir = new File(prefs.getSettingsDir());
- mkdirs(configDir);
- Native.transmissionStop(s, configDir.getAbsolutePath());
- } finally {
- session = STATE_STOPPED;
- torrentFs = null;
- httpServer = null;
- ssdpServer = null;
- watchers = null;
- executor = null;
- scheduler = null;
- semaphores = null;
- suspended = 0;
- wakeUnlock();
- }
- } finally {
- writeLock().unlock();
- }
- }
-
- public boolean isRunning() {
- return session > 0;
- }
-
- @SuppressWarnings("unused")
- public boolean isStarting() {
- return session == STATE_STARTING;
- }
-
- @SuppressWarnings("unused")
- public boolean isStopping() {
- return session == STATE_STOPPING;
- }
-
- public boolean isStopped() {
- return session == STATE_STOPPED;
- }
-
- @SuppressLint("StaticFieldLeak")
- public void suspend(final boolean suspend, final boolean byUser, final Runnable callback) {
- checkRunning();
- new AsyncTask() {
-
- @Override
- protected Void doInBackground(Void... voids) {
- writeLock().lock();
- try {
- if (!isRunning()) return null;
- debug(TAG, "Suspending: %s, by user: %s", suspend, byUser);
-
- if (suspend) {
- Utils.close(ssdpServer);
- ssdpServer = null;
- } else {
- startUpnp();
- }
-
- Native.transmissionSuspend(session, suspend);
- suspended = (byte) (!suspend ? 0 : byUser ? 2 : 1);
- } finally {
- writeLock().unlock();
- }
-
- return null;
- }
-
- @Override
- protected void onPostExecute(Void aVoid) {
- TransmissionService.updateNotification();
- if (callback != null) callback.run();
- }
- }.
-
- execute();
- }
-
- public boolean isSuspended() {
- return suspended != 0;
- }
-
- public boolean isSuspendedByUser() {
- return suspended == 2;
- }
-
- public boolean hasDownloadingTorrents() {
- readLock().lock();
- try {
- return isRunning() && Native.transmissionHasDownloadingTorrents(session);
- } finally {
- readLock().unlock();
- }
- }
-
- public AddTorrentResult addTorrent(File torrentFile, File downloadDir,
- @Nullable int[] unwantedIndexes,
- @Nullable byte[] returnMeTorrentHash,
- boolean delete, boolean sequential,
- int retries, int delay) throws InterruptedException {
- if (!isRunning()) return NOT_STARTED;
- String path = torrentFile.getAbsolutePath();
- String downloadPath = downloadDir.getAbsolutePath();
- info(TAG, "Adding new torrent file: %s", path);
- mkdirs(downloadDir);
-
- for (int i = 0; i < retries + 1; i++) {
- int result;
-
- readLock().lock();
- try {
- if (!isRunning()) {
- info(TAG, "Transmission is not running - ignoring: %s", path);
- return NOT_STARTED;
- }
-
- result = Native.torrentAdd(session, path, downloadPath, delete, sequential,
- unwantedIndexes, returnMeTorrentHash);
- } finally {
- readLock().unlock();
- }
-
- switch (result) {
- case 2:
- info(TAG, "Duplicate torrent - ignoring: %s", torrentFile);
- torrentAddedOrChanged();
- return DUPLICATE;
- case 0:
- torrentAddedOrChanged();
- return OK;
- case 3:
- torrentAddedOrChanged();
- return OK_DELETE;
- case 1:
- Thread.sleep(delay);
- }
- }
-
- err(TAG, "Failed to parse torrent file: %s", torrentFile);
- return PARSE_ERR;
- }
-
- private void torrentAddedOrChanged() {
- debug(TAG, "torrentAddedOrChanged()");
- TorrentFs fs = torrentFs;
- if (fs != null) fs.reset();
- wakeLock();
- }
-
- private void torrentRemoved() {
- debug(TAG, "torrentAddedOrChanged()");
- TorrentFs fs = torrentFs;
- if (fs != null) fs.reset();
- }
-
- private void sessionChanged() {
- debug(TAG, "torrentAddedOrChanged()");
- readLock().lock();
- try {
- if (!isRunning()) return;
- int encrMode = Native.transmissionGetEncryptionMode(session);
-
- if (encrMode != prefs.getEncryptionMode().ordinal()) {
- prefs.setEncryptionMode(EncrMode.get(encrMode));
- }
- } finally {
- readLock().unlock();
- }
- }
-
- private void cheduledAltSpeed() {
- debug(TAG, "Alt speed changed by timer");
- wakeLock();
- }
-
- public Promise magnetToTorrent(final Uri magnetLink, final File destTorrentPath,
- final int timeout, final boolean[] enqueue) {
- return new Promise() {
- private final long sem = Native.semCreate();
-
- {
- List semaphores = Transmission.this.semaphores;
-
- if (semaphores == null) {
- writeLock().lock();
- try {
- if ((semaphores = Transmission.this.semaphores) == null) {
- Transmission.this.semaphores = semaphores = new Vector<>();
- }
- } finally {
- writeLock().unlock();
- }
- }
-
- semaphores.add(sem);
- }
-
- @Override
- public Void get() throws Throwable {
- readLock().lock();
- try {
- checkRunning();
- TorrentFs fs = torrentFs;
- if (fs != null) fs.reset();
- Native.torrentMagnetToTorrentFile(session, sem, magnetLink.toString(),
- destTorrentPath.getAbsolutePath(), timeout, enqueue);
- return null;
- } finally {
- readLock().unlock();
- }
- }
-
- @Override
- public synchronized void cancel() {
- Native.semPost(sem);
- List semaphores = Transmission.this.semaphores;
- if (semaphores != null) semaphores.remove(sem);
- }
-
- @Override
- protected void finalize() {
- List semaphores = Transmission.this.semaphores;
- if (semaphores != null) semaphores.remove(sem);
- Native.semDestroy(sem);
- }
- };
- }
-
- private void increaseSoBuf() {
- if (!prefs.isIncreaseSoBuf()) return;
- String file = "scripts/set_so_buf.sh";
- debug(TAG, "Executing su -c %s", file);
- AssetManager amgr = getContext().getAssets();
- InputStream in = null;
-
- try {
- in = amgr.open(file, AssetManager.ACCESS_STREAMING);
- int status = Utils.su(3000, in);
- if (status != 0) err(TAG, "su -c %s failed with exit code %d", file, status);
- } catch (IOException ex) {
- err(TAG, ex, "Failed to open asset: %s", file);
- } finally {
- if (in != null) try {
- in.close();
- } catch (IOException ignore) {
- }
- }
- }
-
- public ExecutorService getExecutor() {
- ExecutorService exec = executor;
-
- if (exec == null) {
- writeLock().lock();
- try {
- if ((exec = executor) == null) {
- executor = exec = new ThreadPoolExecutor(0,
- 30, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(),
- Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
- }
- } finally {
- writeLock().unlock();
- }
- }
-
- return exec;
- }
-
- private void stopExecutor() {
- ExecutorService exec = executor;
- if (exec == null) return;
- executor = null;
-
- try {
- exec.shutdownNow();
- exec.awaitTermination(30, TimeUnit.SECONDS);
- } catch (InterruptedException ignore) {
- }
- }
-
- public ScheduledExecutorService getScheduler() {
- ScheduledExecutorService sched = scheduler;
-
- if (sched == null) {
- writeLock().lock();
- try {
- if ((sched = scheduler) == null) {
- checkRunning();
- scheduler = sched = Executors.newScheduledThreadPool(1);
- }
- } finally {
- writeLock().unlock();
- }
- }
-
- return sched;
- }
-
- private void stopSheduler() {
- ScheduledExecutorService sched = scheduler;
- if (sched == null) return;
- scheduler = null;
-
- try {
- sched.shutdownNow();
- sched.awaitTermination(30, TimeUnit.SECONDS);
- } catch (InterruptedException ignore) {
- }
- }
-
- @SuppressLint("WakelockTimeout")
- private void wakeLock() {
- writeLock().lock();
- try {
- if (powerLock != null || !isRunning()) return;
- PowerLock pl = PowerLock.newLock(getContext());
- if (pl == null) return;
- pl.acquire();
- powerLock = pl;
- debug(TAG, "WakeLock acquired");
-
- final ScheduledFuture>[] f = new ScheduledFuture[1];
- f[0] = getScheduler().scheduleWithFixedDelay(() -> {
- if (!hasDownloadingTorrents()) {
- writeLock().lock();
- try {
- if (!isRunning()) {
- wakeUnlock();
- f[0].cancel(false);
- } else if (!Native.transmissionHasDownloadingTorrents(session)) {
- debug(TAG, "No active downloads - releasing WakeLock");
- wakeUnlock();
- f[0].cancel(false);
- }
- } finally {
- writeLock().unlock();
- }
- }
- }, 1, 1, TimeUnit.MINUTES);
- } finally {
- writeLock().unlock();
- }
- }
-
- private void wakeUnlock() {
- writeLock().lock();
- try {
- if (powerLock == null) return;
- powerLock.release();
- powerLock = null;
- debug(TAG, "WakeLock released");
- } finally {
- writeLock().unlock();
- }
- }
-
- private final class Watcher extends FileObserver {
- private final File dir;
- private final File downloadDir;
-
- private Watcher(File dir, File downloadDir) {
- super(dir.getAbsolutePath(), FileObserver.CREATE);
- this.dir = dir;
- this.downloadDir = downloadDir;
- }
-
- @Override
- public void startWatching() {
- info(TAG, "Start watching directory: %s", dir);
- mkdirs(dir);
- scan();
- super.startWatching();
- }
-
- void scan() {
- String[] files = dir.list();
- if (files != null) for (String f : files) add(f);
- }
-
- @Override
- public void onEvent(int event, @Nullable String path) {
- add(path);
- }
-
- private void add(String path) {
- if ((path == null) || !path.endsWith(".torrent")) return;
- File f = new File(dir, path);
- AddTorrentResult result;
-
- try {
- result = addTorrent(f, downloadDir, null, null,
- false, prefs.isSeqDownloadEnabled(), 10, 1000);
- } catch (InterruptedException ex) {
- err(TAG, ex, "Failed to add torrent file: %s", f);
- return;
- }
-
- if (result == OK) {
- File renameTo = new File(dir, path + ".added");
- if (!f.renameTo(renameTo) &&
- !StorageAccess.renamePath(f.getAbsolutePath(), renameTo.getAbsolutePath())) {
- err(TAG, "Failed to rename file to: %s", renameTo);
- }
- } else if (result != NOT_STARTED) {
- if (!f.delete() && !StorageAccess.removePath(f.getAbsolutePath())) {
- err(TAG, "Failed to delete file: %s", f);
- }
- }
- }
- }
-
- void checkRunning() {
- if (!isRunning()) {
- throw new IllegalStateException("Transmission is not running");
- }
- }
-
- public enum AddTorrentResult {
- OK, PARSE_ERR, DUPLICATE, OK_DELETE, NOT_STARTED
- }
+ }
+ }
+
+ private void startWatcher(String watchDir, String downloadDir) {
+ File wd = new File(watchDir);
+ mkdirs(wd);
+ Watcher w = new Watcher(wd, new File(downloadDir));
+ w.startWatching();
+ watchers.add(w);
+ }
+
+ private void startUpnp() {
+ if (!prefs.isUpnpEnabled()) return;
+ if ((ssdpServer != null) && ssdpServer.isRunning()) return;
+ HttpServer httpServer;
+
+ try {
+ httpServer = getHttpServer();
+ } catch (IOException ex) {
+ err(TAG, ex, "Failed to start HTTP Server");
+ return;
+ }
+
+ try {
+ if (ssdpServer == null) ssdpServer = new SsdpServer(httpServer);
+ ssdpServer.start();
+ } catch (IOException ex) {
+ err(TAG, ex, "Failed to start SSDP server, SSDP NOTIFY will be sent every 60 seconds");
+ }
+ }
+
+ public void stop() {
+ long s = session;
+ if (s == 0) return;
+
+ writeLock().lock();
+ try {
+ if ((s = session) == 0) return;
+ state = STATE_STOPPING;
+
+ try {
+ if (semaphores != null) for (Long sem : semaphores) Native.semPost(sem);
+ if (watchers != null) for (Watcher w : watchers) w.stopWatching();
+ ConnectivityChangeReceiver.unregister(getContext());
+ Native.transmissionSetRpcCallbacks(null, null, null, null);
+ Utils.close(httpServer, ssdpServer);
+ stopExecutor();
+ stopSheduler();
+
+ debug(TAG, "Closing session: %d", s);
+ File configDir = new File(prefs.getSettingsDir());
+ mkdirs(configDir);
+ Native.transmissionStop(s, configDir.getAbsolutePath());
+ } finally {
+ session = 0;
+ state = STATE_STOPPED;
+ torrentFs = null;
+ httpServer = null;
+ ssdpServer = null;
+ watchers = null;
+ executor = null;
+ scheduler = null;
+ semaphores = null;
+ wakeUnlock();
+ }
+ } finally {
+ writeLock().unlock();
+ }
+ }
+
+ public boolean isRunning() {
+ return (state & STATE_RUNNING) != 0;
+ }
+
+ @SuppressWarnings("unused")
+ public boolean isStarting() {
+ return (state & STATE_STARTING) != 0;
+ }
+
+ @SuppressWarnings("unused")
+ public boolean isStopping() {
+ return (state & STATE_STOPPING) != 0;
+ }
+
+ public boolean isStopped() {
+ return state == STATE_STOPPED;
+ }
+
+ @SuppressLint("StaticFieldLeak")
+ public void suspend(final boolean suspend, final boolean byUser, final Runnable callback) {
+ checkRunning();
+ new AsyncTask() {
+
+ @Override
+ protected Void doInBackground(Void... voids) {
+ writeLock().lock();
+ try {
+ if (!isRunning()) return null;
+ debug(TAG, "Suspending: %s, by user: %s", suspend, byUser);
+
+ if (suspend) {
+ Utils.close(ssdpServer);
+ ssdpServer = null;
+ } else {
+ startUpnp();
+ }
+
+ Native.transmissionSuspend(session, suspend);
+ state &= ~(STATE_SUSPENDED | STATE_SUSPENDED_BY_USER);
+ if (suspend) state |= byUser ? STATE_SUSPENDED_BY_USER : STATE_SUSPENDED;
+ } finally {
+ writeLock().unlock();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void aVoid) {
+ TransmissionService.updateNotification();
+ if (callback != null) callback.run();
+ }
+ }.
+
+ execute();
+ }
+
+ public boolean isSuspended() {
+ return (state & (STATE_SUSPENDED | STATE_SUSPENDED_BY_USER)) != 0;
+ }
+
+ public boolean isSuspendedByUser() {
+ return (state & STATE_SUSPENDED_BY_USER) != 0;
+ }
+
+ public boolean hasDownloadingTorrents() {
+ readLock().lock();
+ try {
+ return isRunning() && Native.transmissionHasDownloadingTorrents(session);
+ } finally {
+ readLock().unlock();
+ }
+ }
+
+ public AddTorrentResult addTorrent(File torrentFile, File downloadDir,
+ @Nullable int[] unwantedIndexes,
+ @Nullable byte[] returnMeTorrentHash,
+ boolean delete, boolean sequential,
+ int retries, int delay) throws InterruptedException {
+ if (!isRunning()) return NOT_STARTED;
+ String path = torrentFile.getAbsolutePath();
+ String downloadPath = downloadDir.getAbsolutePath();
+ info(TAG, "Adding new torrent file: %s", path);
+ mkdirs(downloadDir);
+
+ for (int i = 0; i < retries + 1; i++) {
+ int result;
+
+ readLock().lock();
+ try {
+ if (!isRunning()) {
+ info(TAG, "Transmission is not running - ignoring: %s", path);
+ return NOT_STARTED;
+ }
+
+ result = Native.torrentAdd(session, path, downloadPath, delete, sequential,
+ unwantedIndexes, returnMeTorrentHash);
+ } finally {
+ readLock().unlock();
+ }
+
+ switch (result) {
+ case 2:
+ info(TAG, "Duplicate torrent - ignoring: %s", torrentFile);
+ torrentAddedOrChanged();
+ return DUPLICATE;
+ case 0:
+ torrentAddedOrChanged();
+ return OK;
+ case 3:
+ torrentAddedOrChanged();
+ return OK_DELETE;
+ case 1:
+ Thread.sleep(delay);
+ }
+ }
+
+ err(TAG, "Failed to parse torrent file: %s", torrentFile);
+ return PARSE_ERR;
+ }
+
+ private void torrentAddedOrChanged() {
+ debug(TAG, "torrentAddedOrChanged()");
+ TorrentFs fs = torrentFs;
+ if (fs != null) fs.reset();
+ wakeLock();
+ }
+
+ private void torrentRemoved() {
+ debug(TAG, "torrentAddedOrChanged()");
+ TorrentFs fs = torrentFs;
+ if (fs != null) fs.reset();
+ }
+
+ private void sessionChanged() {
+ debug(TAG, "torrentAddedOrChanged()");
+ readLock().lock();
+ try {
+ if (!isRunning()) return;
+ int encrMode = Native.transmissionGetEncryptionMode(session);
+
+ if (encrMode != prefs.getEncryptionMode().ordinal()) {
+ prefs.setEncryptionMode(EncrMode.get(encrMode));
+ }
+ } finally {
+ readLock().unlock();
+ }
+ }
+
+ private void cheduledAltSpeed() {
+ debug(TAG, "Alt speed changed by timer");
+ wakeLock();
+ }
+
+ public Promise magnetToTorrent(final Uri magnetLink, final File destTorrentPath,
+ final int timeout, final boolean[] enqueue) {
+ return new Promise() {
+ private final long sem = Native.semCreate();
+
+ {
+ List semaphores = Transmission.this.semaphores;
+
+ if (semaphores == null) {
+ writeLock().lock();
+ try {
+ if ((semaphores = Transmission.this.semaphores) == null) {
+ Transmission.this.semaphores = semaphores = new Vector<>();
+ }
+ } finally {
+ writeLock().unlock();
+ }
+ }
+
+ semaphores.add(sem);
+ }
+
+ @Override
+ public Void get() throws Throwable {
+ readLock().lock();
+ try {
+ checkRunning();
+ TorrentFs fs = torrentFs;
+ if (fs != null) fs.reset();
+ Native.torrentMagnetToTorrentFile(session, sem, magnetLink.toString(),
+ destTorrentPath.getAbsolutePath(), timeout, enqueue);
+ return null;
+ } finally {
+ readLock().unlock();
+ }
+ }
+
+ @Override
+ public synchronized void cancel() {
+ Native.semPost(sem);
+ List semaphores = Transmission.this.semaphores;
+ if (semaphores != null) semaphores.remove(sem);
+ }
+
+ @Override
+ protected void finalize() {
+ List semaphores = Transmission.this.semaphores;
+ if (semaphores != null) semaphores.remove(sem);
+ Native.semDestroy(sem);
+ }
+ };
+ }
+
+ private void increaseSoBuf() {
+ if (!prefs.isIncreaseSoBuf()) return;
+ String file = "scripts/set_so_buf.sh";
+ debug(TAG, "Executing su -c %s", file);
+ AssetManager amgr = getContext().getAssets();
+ InputStream in = null;
+
+ try {
+ in = amgr.open(file, AssetManager.ACCESS_STREAMING);
+ int status = Utils.su(3000, in);
+ if (status != 0) err(TAG, "su -c %s failed with exit code %d", file, status);
+ } catch (IOException ex) {
+ err(TAG, ex, "Failed to open asset: %s", file);
+ } finally {
+ if (in != null) try {
+ in.close();
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
+ public ExecutorService getExecutor() {
+ ExecutorService exec = executor;
+
+ if (exec == null) {
+ writeLock().lock();
+ try {
+ if ((exec = executor) == null) {
+ executor = exec = new ThreadPoolExecutor(0,
+ 30, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(),
+ Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
+ }
+ } finally {
+ writeLock().unlock();
+ }
+ }
+
+ return exec;
+ }
+
+ private void stopExecutor() {
+ ExecutorService exec = executor;
+ if (exec == null) return;
+ executor = null;
+
+ try {
+ exec.shutdownNow();
+ exec.awaitTermination(30, TimeUnit.SECONDS);
+ } catch (InterruptedException ignore) {
+ }
+ }
+
+ public ScheduledExecutorService getScheduler() {
+ ScheduledExecutorService sched = scheduler;
+
+ if (sched == null) {
+ writeLock().lock();
+ try {
+ if ((sched = scheduler) == null) {
+ checkRunning();
+ scheduler = sched = Executors.newScheduledThreadPool(1);
+ }
+ } finally {
+ writeLock().unlock();
+ }
+ }
+
+ return sched;
+ }
+
+ private void stopSheduler() {
+ ScheduledExecutorService sched = scheduler;
+ if (sched == null) return;
+ scheduler = null;
+
+ try {
+ sched.shutdownNow();
+ sched.awaitTermination(30, TimeUnit.SECONDS);
+ } catch (InterruptedException ignore) {
+ }
+ }
+
+ @SuppressLint("WakelockTimeout")
+ private void wakeLock() {
+ writeLock().lock();
+ try {
+ if (powerLock != null || !isRunning()) return;
+ PowerLock pl = PowerLock.newLock(getContext());
+ if (pl == null) return;
+ pl.acquire();
+ powerLock = pl;
+ debug(TAG, "WakeLock acquired");
+
+ final ScheduledFuture>[] f = new ScheduledFuture[1];
+ f[0] = getScheduler().scheduleWithFixedDelay(() -> {
+ if (!hasDownloadingTorrents()) {
+ writeLock().lock();
+ try {
+ if (!isRunning()) {
+ wakeUnlock();
+ f[0].cancel(false);
+ } else if (!Native.transmissionHasDownloadingTorrents(session)) {
+ debug(TAG, "No active downloads - releasing WakeLock");
+ wakeUnlock();
+ f[0].cancel(false);
+ }
+ } finally {
+ writeLock().unlock();
+ }
+ }
+ }, 1, 1, TimeUnit.MINUTES);
+ } finally {
+ writeLock().unlock();
+ }
+ }
+
+ private void wakeUnlock() {
+ writeLock().lock();
+ try {
+ if (powerLock == null) return;
+ powerLock.release();
+ powerLock = null;
+ debug(TAG, "WakeLock released");
+ } finally {
+ writeLock().unlock();
+ }
+ }
+
+ private final class Watcher extends FileObserver {
+ private final File dir;
+ private final File downloadDir;
+
+ private Watcher(File dir, File downloadDir) {
+ super(dir.getAbsolutePath(), FileObserver.CREATE);
+ this.dir = dir;
+ this.downloadDir = downloadDir;
+ }
+
+ @Override
+ public void startWatching() {
+ info(TAG, "Start watching directory: %s", dir);
+ mkdirs(dir);
+ scan();
+ super.startWatching();
+ }
+
+ void scan() {
+ String[] files = dir.list();
+ if (files != null) for (String f : files) add(f);
+ }
+
+ @Override
+ public void onEvent(int event, @Nullable String path) {
+ add(path);
+ }
+
+ private void add(String path) {
+ if ((path == null) || !path.endsWith(".torrent")) return;
+ File f = new File(dir, path);
+ AddTorrentResult result;
+
+ try {
+ result = addTorrent(f, downloadDir, null, null,
+ false, prefs.isSeqDownloadEnabled(), 10, 1000);
+ } catch (InterruptedException ex) {
+ err(TAG, ex, "Failed to add torrent file: %s", f);
+ return;
+ }
+
+ if (result == OK) {
+ File renameTo = new File(dir, path + ".added");
+ if (!f.renameTo(renameTo) &&
+ !StorageAccess.renamePath(f.getAbsolutePath(), renameTo.getAbsolutePath())) {
+ err(TAG, "Failed to rename file to: %s", renameTo);
+ }
+ } else if (result != NOT_STARTED) {
+ if (!f.delete() && !StorageAccess.removePath(f.getAbsolutePath())) {
+ err(TAG, "Failed to delete file: %s", f);
+ }
+ }
+ }
+ }
+
+ void checkRunning() {
+ if (!isRunning()) {
+ throw new IllegalStateException("Transmission is not running");
+ }
+ }
+
+ public enum AddTorrentResult {
+ OK, PARSE_ERR, DUPLICATE, OK_DELETE, NOT_STARTED
+ }
}