From 9db3d07b9620b4269ab33f78604a36327e536ce1 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Queru Date: Thu, 12 Nov 2009 18:45:53 -0800 Subject: eclair snapshot --- .../jni/.android_server_BluetoothEventLoop.cpp.swp | Bin 0 -> 16384 bytes core/jni/ActivityManager.cpp | 6 +- core/jni/Android.mk | 16 +- core/jni/AndroidRuntime.cpp | 226 +++-- core/jni/CursorWindow.cpp | 2 +- core/jni/CursorWindow.h | 2 +- core/jni/android/graphics/Bitmap.cpp | 17 +- core/jni/android/graphics/BitmapFactory.cpp | 7 +- core/jni/android/graphics/Canvas.cpp | 3 +- core/jni/android/graphics/ColorFilter.cpp | 6 +- core/jni/android/graphics/Graphics.cpp | 6 +- core/jni/android/graphics/GraphicsJNI.h | 2 +- core/jni/android/graphics/MaskFilter.cpp | 21 + core/jni/android/graphics/NinePatch.cpp | 29 + core/jni/android/graphics/NinePatchImpl.cpp | 40 +- core/jni/android/graphics/Paint.cpp | 23 +- core/jni/android/graphics/Path.cpp | 5 +- core/jni/android/graphics/Region.cpp | 2 +- core/jni/android/graphics/Typeface.cpp | 22 +- core/jni/android/opengl/util.cpp | 141 +-- core/jni/android_bluetooth_BluetoothSocket.cpp | 557 +++++++++++ core/jni/android_bluetooth_Database.cpp | 184 ---- core/jni/android_bluetooth_HeadsetBase.cpp | 28 +- core/jni/android_bluetooth_RfcommSocket.cpp | 621 ------------ core/jni/android_bluetooth_common.cpp | 332 ++++++- core/jni/android_bluetooth_common.h | 19 +- core/jni/android_database_CursorWindow.cpp | 65 +- core/jni/android_database_SQLiteDatabase.cpp | 5 +- core/jni/android_emoji_EmojiFactory.cpp | 7 +- core/jni/android_hardware_Camera.cpp | 201 +++- core/jni/android_location_GpsLocationProvider.cpp | 165 +++- core/jni/android_media_AudioRecord.cpp | 26 +- core/jni/android_media_AudioSystem.cpp | 166 ++-- core/jni/android_media_AudioTrack.cpp | 49 +- core/jni/android_media_ToneGenerator.cpp | 8 +- core/jni/android_net_NetUtils.cpp | 12 + core/jni/android_net_wifi_Wifi.cpp | 24 +- core/jni/android_opengl_GLES11.cpp | 12 + core/jni/android_os_Debug.cpp | 12 +- core/jni/android_os_Exec.cpp | 215 ---- core/jni/android_os_MemoryFile.cpp | 12 +- core/jni/android_os_SystemProperties.cpp | 109 ++- core/jni/android_server_BluetoothA2dpService.cpp | 308 ++---- core/jni/android_server_BluetoothDeviceService.cpp | 1035 -------------------- core/jni/android_server_BluetoothEventLoop.cpp | 850 ++++++++-------- core/jni/android_server_BluetoothService.cpp | 915 +++++++++++++++++ core/jni/android_text_format_Time.cpp | 10 +- core/jni/android_util_Binder.cpp | 10 +- core/jni/android_util_Binder.h | 2 +- core/jni/android_util_EventLog.cpp | 92 +- core/jni/android_util_Process.cpp | 92 +- core/jni/android_view_Surface.cpp | 307 +++--- core/jni/com_google_android_gles_jni_EGLImpl.cpp | 3 +- core/jni/com_google_android_gles_jni_GLImpl.cpp | 12 + 54 files changed, 3788 insertions(+), 3253 deletions(-) create mode 100644 core/jni/.android_server_BluetoothEventLoop.cpp.swp create mode 100644 core/jni/android_bluetooth_BluetoothSocket.cpp delete mode 100644 core/jni/android_bluetooth_Database.cpp delete mode 100644 core/jni/android_bluetooth_RfcommSocket.cpp delete mode 100644 core/jni/android_os_Exec.cpp delete mode 100644 core/jni/android_server_BluetoothDeviceService.cpp create mode 100644 core/jni/android_server_BluetoothService.cpp (limited to 'core/jni') diff --git a/core/jni/.android_server_BluetoothEventLoop.cpp.swp b/core/jni/.android_server_BluetoothEventLoop.cpp.swp new file mode 100644 index 0000000..d36e403 Binary files /dev/null and b/core/jni/.android_server_BluetoothEventLoop.cpp.swp differ diff --git a/core/jni/ActivityManager.cpp b/core/jni/ActivityManager.cpp index 9017827..8950dfb 100644 --- a/core/jni/ActivityManager.cpp +++ b/core/jni/ActivityManager.cpp @@ -16,9 +16,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include namespace android { diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 888cb11..015268b 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -11,10 +11,16 @@ else LOCAL_CFLAGS += -DPACKED="" endif +ifeq ($(WITH_JIT),true) + LOCAL_CFLAGS += -DWITH_JIT +endif + ifneq ($(USE_CUSTOM_RUNTIME_HEAP_MAX),) LOCAL_CFLAGS += -DCUSTOM_RUNTIME_HEAP_MAX=$(USE_CUSTOM_RUNTIME_HEAP_MAX) endif +LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES + LOCAL_SRC_FILES:= \ ActivityManager.cpp \ AndroidRuntime.cpp \ @@ -39,7 +45,6 @@ LOCAL_SRC_FILES:= \ android_text_AndroidCharacter.cpp \ android_text_KeyCharacterMap.cpp \ android_os_Debug.cpp \ - android_os_Exec.cpp \ android_os_FileUtils.cpp \ android_os_MemoryFile.cpp \ android_os_ParcelFileDescriptor.cpp \ @@ -104,13 +109,12 @@ LOCAL_SRC_FILES:= \ android_util_FileObserver.cpp \ android/opengl/poly_clip.cpp.arm \ android/opengl/util.cpp.arm \ - android_bluetooth_Database.cpp \ android_bluetooth_HeadsetBase.cpp \ android_bluetooth_common.cpp \ android_bluetooth_BluetoothAudioGateway.cpp \ - android_bluetooth_RfcommSocket.cpp \ + android_bluetooth_BluetoothSocket.cpp \ android_bluetooth_ScoSocket.cpp \ - android_server_BluetoothDeviceService.cpp \ + android_server_BluetoothService.cpp \ android_server_BluetoothEventLoop.cpp \ android_server_BluetoothA2dpService.cpp \ android_message_digest_sha1.cpp \ @@ -150,11 +154,11 @@ LOCAL_SHARED_LIBRARIES := \ libnativehelper \ libcutils \ libutils \ + libbinder \ libnetutils \ libui \ libskiagl \ - libsgl \ - libcorecg \ + libskia \ libsqlite \ libdvm \ libEGL \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index c815301..f61e247 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -19,12 +19,12 @@ //#define LOG_NDEBUG 0 #include -#include -#include +#include +#include #include #include -#include -#include +#include +#include #include #include @@ -130,7 +130,6 @@ extern int register_android_os_Power(JNIEnv *env); extern int register_android_os_StatFs(JNIEnv *env); extern int register_android_os_SystemProperties(JNIEnv *env); extern int register_android_os_Hardware(JNIEnv* env); -extern int register_android_os_Exec(JNIEnv *env); extern int register_android_os_SystemClock(JNIEnv* env); extern int register_android_os_FileObserver(JNIEnv *env); extern int register_android_os_FileUtils(JNIEnv *env); @@ -143,12 +142,11 @@ extern int register_android_security_Md5MessageDigest(JNIEnv *env); extern int register_android_text_AndroidCharacter(JNIEnv *env); extern int register_android_text_KeyCharacterMap(JNIEnv *env); extern int register_android_opengl_classes(JNIEnv *env); -extern int register_android_bluetooth_Database(JNIEnv* env); extern int register_android_bluetooth_HeadsetBase(JNIEnv* env); extern int register_android_bluetooth_BluetoothAudioGateway(JNIEnv* env); -extern int register_android_bluetooth_RfcommSocket(JNIEnv *env); +extern int register_android_bluetooth_BluetoothSocket(JNIEnv *env); extern int register_android_bluetooth_ScoSocket(JNIEnv *env); -extern int register_android_server_BluetoothDeviceService(JNIEnv* env); +extern int register_android_server_BluetoothService(JNIEnv* env); extern int register_android_server_BluetoothEventLoop(JNIEnv *env); extern int register_android_server_BluetoothA2dpService(JNIEnv* env); extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); @@ -509,11 +507,17 @@ static void readLocale(char* language, char* region) //LOGD("language=%s region=%s\n", language, region); } -void AndroidRuntime::start(const char* className, const bool startSystemServer) +/* + * Start the Dalvik Virtual Machine. + * + * Various arguments, most determined by system properties, are passed in. + * The "mOptions" vector is updated. + * + * Returns 0 on success. + */ +int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) { - LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n"); - - JNIEnv* env; + int result = -1; JavaVMInitArgs initArgs; JavaVMOption opt; char propBuf[PROPERTY_VALUE_MAX]; @@ -521,25 +525,20 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) char dexoptFlagsBuf[PROPERTY_VALUE_MAX]; char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX]; char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX]; + char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX]; char* stackTraceFile = NULL; - char* slashClassName = NULL; - char* cp; bool checkJni = false; + bool checkDexSum = false; bool logStdio = false; - enum { kEMDefault, kEMIntPortable, kEMIntFast } executionMode = kEMDefault; + enum { + kEMDefault, + kEMIntPortable, + kEMIntFast, +#if defined(WITH_JIT) + kEMJitCompiler, +#endif + } executionMode = kEMDefault; - blockSigpipe(); - - /* - * 'startSystemServer == true' means runtime is obslete and not run from - * init.rc anymore, so we print out the boot start event here. - */ - if (startSystemServer) { - /* track our progress through the boot sequence */ - const int LOG_BOOT_PROGRESS_START = 3000; - LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, - ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); - } property_get("dalvik.vm.checkjni", propBuf, ""); if (strcmp(propBuf, "true") == 0) { @@ -557,10 +556,19 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) executionMode = kEMIntPortable; } else if (strcmp(propBuf, "int:fast") == 0) { executionMode = kEMIntFast; +#if defined(WITH_JIT) + } else if (strcmp(propBuf, "int:jit") == 0) { + executionMode = kEMJitCompiler; +#endif } property_get("dalvik.vm.stack-trace-file", stackTraceFileBuf, ""); + property_get("dalvik.vm.check-dex-sum", propBuf, ""); + if (strcmp(propBuf, "true") == 0) { + checkDexSum = true; + } + property_get("log.redirect-stdio", propBuf, ""); if (strcmp(propBuf, "true") == 0) { logStdio = true; @@ -572,19 +580,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) strcpy(jniOptsBuf, "-Xjniopts:"); property_get("dalvik.vm.jniopts", jniOptsBuf+10, ""); - const char* rootDir = getenv("ANDROID_ROOT"); - if (rootDir == NULL) { - rootDir = "/system"; - if (!hasDir("/system")) { - LOG_FATAL("No root directory specified, and /android does not exist."); - return; - } - setenv("ANDROID_ROOT", rootDir, 1); - } - - const char* kernelHack = getenv("LD_ASSUME_KERNEL"); - //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack); - /* route exit() to our handler */ opt.extraInfo = (void*) runtime_exit; opt.optionString = "exit"; @@ -603,16 +598,10 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) mOptions.add(opt); //options[curOpt++].optionString = "-verbose:class"; -#ifdef CUSTOM_RUNTIME_HEAP_MAX -#define __make_max_heap_opt(val) #val -#define _make_max_heap_opt(val) "-Xmx" __make_max_heap_opt(val) - opt.optionString = _make_max_heap_opt(CUSTOM_RUNTIME_HEAP_MAX); -#undef __make_max_heap_opt -#undef _make_max_heap_opt -#else - /* limit memory use to 16MB */ - opt.optionString = "-Xmx16m"; -#endif + strcpy(heapsizeOptsBuf, "-Xmx"); + property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m"); + //LOGI("Heap size: %s", heapsizeOptsBuf); + opt.optionString = heapsizeOptsBuf; mOptions.add(opt); /* @@ -658,6 +647,10 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) if (opc != NULL) { opt.optionString = "-Xgenregmap"; mOptions.add(opt); + + /* turn on precise GC while we're at it */ + opt.optionString = "-Xgc:precise"; + mOptions.add(opt); } } @@ -697,13 +690,87 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) //opt.optionString = "-verbose:jni"; //mOptions.add(opt); } + +#if defined(WITH_JIT) + /* Minimal profile threshold to trigger JIT compilation */ + char jitThresholdBuf[sizeof("-Xthreshold:") + PROPERTY_VALUE_MAX]; + property_get("dalvik.vm.jit.threshold", propBuf, ""); + if (strlen(propBuf) > 0) { + strcpy(jitThresholdBuf, "-Xthreshold:"); + strcat(jitThresholdBuf, propBuf); + opt.optionString = jitThresholdBuf; + mOptions.add(opt); + } + + /* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */ + char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX]; + property_get("dalvik.vm.jit.op", propBuf, ""); + if (strlen(propBuf) > 0) { + strcpy(jitOpBuf, "-Xjitop:"); + strcat(jitOpBuf, propBuf); + opt.optionString = jitOpBuf; + mOptions.add(opt); + } + + /* + * Reverse the polarity of dalvik.vm.jit.op and force interpreter-only + * for non-selected opcodes. + */ + property_get("dalvik.vm.jit.includeop", propBuf, ""); + if (strlen(propBuf) > 0) { + opt.optionString = "-Xincludeselectedop"; + mOptions.add(opt); + } + + /* Force interpreter-only mode for selected methods */ + char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX]; + property_get("dalvik.vm.jit.method", propBuf, ""); + if (strlen(propBuf) > 0) { + strcpy(jitMethodBuf, "-Xjitmethod:"); + strcat(jitMethodBuf, propBuf); + opt.optionString = jitMethodBuf; + mOptions.add(opt); + } + + /* + * Reverse the polarity of dalvik.vm.jit.method and force interpreter-only + * for non-selected methods. + */ + property_get("dalvik.vm.jit.includemethod", propBuf, ""); + if (strlen(propBuf) > 0) { + opt.optionString = "-Xincludeselectedmethod"; + mOptions.add(opt); + } + + /* + * Enable profile collection on JIT'ed code. + */ + property_get("dalvik.vm.jit.profile", propBuf, ""); + if (strlen(propBuf) > 0) { + opt.optionString = "-Xjitprofile"; + mOptions.add(opt); + } +#endif + if (executionMode == kEMIntPortable) { opt.optionString = "-Xint:portable"; mOptions.add(opt); } else if (executionMode == kEMIntFast) { opt.optionString = "-Xint:fast"; mOptions.add(opt); +#if defined(WITH_JIT) + } else if (executionMode == kEMJitCompiler) { + opt.optionString = "-Xint:jit"; + mOptions.add(opt); +#endif } + + if (checkDexSum) { + /* perform additional DEX checksum tests */ + opt.optionString = "-Xcheckdexsum"; + mOptions.add(opt); + } + if (logStdio) { /* convert stdout/stderr to log messages */ opt.optionString = "-Xlog-stdio"; @@ -771,11 +838,61 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) * If this call succeeds, the VM is ready, and we can start issuing * JNI calls. */ - if (JNI_CreateJavaVM(&mJavaVM, &env, &initArgs) < 0) { + if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { LOGE("JNI_CreateJavaVM failed\n"); goto bail; } + result = 0; + +bail: + free(stackTraceFile); + return result; +} + +/* + * Start the Android runtime. This involves starting the virtual machine + * and calling the "static void main(String[] args)" method in the class + * named by "className". + */ +void AndroidRuntime::start(const char* className, const bool startSystemServer) +{ + LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n"); + + char* slashClassName = NULL; + char* cp; + JNIEnv* env; + + blockSigpipe(); + + /* + * 'startSystemServer == true' means runtime is obslete and not run from + * init.rc anymore, so we print out the boot start event here. + */ + if (startSystemServer) { + /* track our progress through the boot sequence */ + const int LOG_BOOT_PROGRESS_START = 3000; + LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, + ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); + } + + const char* rootDir = getenv("ANDROID_ROOT"); + if (rootDir == NULL) { + rootDir = "/system"; + if (!hasDir("/system")) { + LOG_FATAL("No root directory specified, and /android does not exist."); + goto bail; + } + setenv("ANDROID_ROOT", rootDir, 1); + } + + //const char* kernelHack = getenv("LD_ASSUME_KERNEL"); + //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack); + + /* start the virtual machine */ + if (startVm(&mJavaVM, &env) != 0) + goto bail; + /* * Register android functions. */ @@ -845,7 +962,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) bail: free(slashClassName); - free(stackTraceFile); } void AndroidRuntime::start() @@ -1095,7 +1211,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_database_SQLiteQuery), REG_JNI(register_android_database_SQLiteStatement), REG_JNI(register_android_os_Debug), - REG_JNI(register_android_os_Exec), REG_JNI(register_android_os_FileObserver), REG_JNI(register_android_os_FileUtils), REG_JNI(register_android_os_ParcelFileDescriptor), @@ -1117,12 +1232,11 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_media_ToneGenerator), REG_JNI(register_android_opengl_classes), - REG_JNI(register_android_bluetooth_Database), REG_JNI(register_android_bluetooth_HeadsetBase), REG_JNI(register_android_bluetooth_BluetoothAudioGateway), - REG_JNI(register_android_bluetooth_RfcommSocket), + REG_JNI(register_android_bluetooth_BluetoothSocket), REG_JNI(register_android_bluetooth_ScoSocket), - REG_JNI(register_android_server_BluetoothDeviceService), + REG_JNI(register_android_server_BluetoothService), REG_JNI(register_android_server_BluetoothEventLoop), REG_JNI(register_android_server_BluetoothA2dpService), REG_JNI(register_android_message_digest_sha1), diff --git a/core/jni/CursorWindow.cpp b/core/jni/CursorWindow.cpp index fb891c9..7864189 100644 --- a/core/jni/CursorWindow.cpp +++ b/core/jni/CursorWindow.cpp @@ -18,7 +18,7 @@ #define LOG_TAG "CursorWindow" #include -#include +#include #include #include diff --git a/core/jni/CursorWindow.h b/core/jni/CursorWindow.h index 0fb074f..e98b009 100644 --- a/core/jni/CursorWindow.h +++ b/core/jni/CursorWindow.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 3fb07a7..0f1b845 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -5,7 +5,7 @@ #include "SkDither.h" #include "SkUnPreMultiply.h" -#include "Parcel.h" +#include #include "android_util_Binder.h" #include "android_nio_utils.h" #include "CreateJavaOutputStreamAdaptor.h" @@ -317,6 +317,11 @@ static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) { return !bitmap->isOpaque(); } +static void Bitmap_setHasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap, + jboolean hasAlpha) { + bitmap->setIsOpaque(!hasAlpha); +} + /////////////////////////////////////////////////////////////////////////////// static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { @@ -332,6 +337,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { const int width = p->readInt32(); const int height = p->readInt32(); const int rowBytes = p->readInt32(); + const int density = p->readInt32(); if (SkBitmap::kARGB_8888_Config != config && SkBitmap::kRGB_565_Config != config && @@ -369,12 +375,13 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { memcpy(bitmap->getPixels(), p->readInplace(size), size); bitmap->unlockPixels(); - return GraphicsJNI::createBitmap(env, bitmap, isMutable, NULL); + return GraphicsJNI::createBitmap(env, bitmap, isMutable, NULL, density); } static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, const SkBitmap* bitmap, - jboolean isMutable, jobject parcel) { + jboolean isMutable, jint density, + jobject parcel) { if (parcel == NULL) { SkDebugf("------- writeToParcel null parcel\n"); return false; @@ -387,6 +394,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, p->writeInt32(bitmap->width()); p->writeInt32(bitmap->height()); p->writeInt32(bitmap->rowBytes()); + p->writeInt32(density); if (bitmap->getConfig() == SkBitmap::kIndex8_Config) { SkColorTable* ctable = bitmap->getColorTable(); @@ -543,10 +551,11 @@ static JNINativeMethod gBitmapMethods[] = { { "nativeRowBytes", "(I)I", (void*)Bitmap_rowBytes }, { "nativeConfig", "(I)I", (void*)Bitmap_config }, { "nativeHasAlpha", "(I)Z", (void*)Bitmap_hasAlpha }, + { "nativeSetHasAlpha", "(IZ)V", (void*)Bitmap_setHasAlpha }, { "nativeCreateFromParcel", "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", (void*)Bitmap_createFromParcel }, - { "nativeWriteToParcel", "(IZLandroid/os/Parcel;)Z", + { "nativeWriteToParcel", "(IZILandroid/os/Parcel;)Z", (void*)Bitmap_writeToParcel }, { "nativeExtractAlpha", "(II[I)Landroid/graphics/Bitmap;", (void*)Bitmap_extractAlpha }, diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 0c84265..65f6845 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -314,14 +314,15 @@ static jobject nullObjectReturn(const char msg[]) { } static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, - int sampleSize) { - SkPixelRef* pr; + int sampleSize, bool ditherImage) { + SkImageRef* pr; // only use ashmem for large images, since mmaps come at a price if (bitmap->getSize() >= 32 * 1024) { pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize); } else { pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize); } + pr->setDitherImage(ditherImage); bitmap->setPixelRef(pr)->unref(); return pr; } @@ -440,7 +441,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, SkPixelRef* pr; if (isPurgeable) { - pr = installPixelRef(bitmap, stream, sampleSize); + pr = installPixelRef(bitmap, stream, sampleSize, doDither); } else { // if we get here, we're in kDecodePixels_Mode and will therefore // already have a pixelref installed. diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 1c2e055..dc72008 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -23,6 +23,7 @@ #include "SkGLCanvas.h" #include "SkGraphics.h" #include "SkImageRef_GlobalPool.h" +#include "SkPorterDuff.h" #include "SkShader.h" #include "SkTemplates.h" @@ -324,7 +325,7 @@ public: static void drawColor__II(JNIEnv* env, jobject, SkCanvas* canvas, jint color, SkPorterDuff::Mode mode) { - canvas->drawColor(color, mode); + canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode)); } static void drawPaint(JNIEnv* env, jobject, SkCanvas* canvas, diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp index b6ec4a2..ebfb209 100644 --- a/core/jni/android/graphics/ColorFilter.cpp +++ b/core/jni/android/graphics/ColorFilter.cpp @@ -21,6 +21,7 @@ #include "SkColorFilter.h" #include "SkColorMatrixFilter.h" +#include "SkPorterDuff.h" namespace android { @@ -32,8 +33,9 @@ public: } static SkColorFilter* CreatePorterDuffFilter(JNIEnv* env, jobject, - jint srcColor, SkPorterDuff::Mode porterDuffMode) { - return SkColorFilter::CreatePorterDuffFilter(srcColor, porterDuffMode); + jint srcColor, SkPorterDuff::Mode mode) { + return SkColorFilter::CreateModeFilter(srcColor, + SkPorterDuff::ToXfermodeMode(mode)); } static SkColorFilter* CreateLightingFilter(JNIEnv* env, jobject, diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index ca1cb7d..2e0caed 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -351,7 +351,7 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) /////////////////////////////////////////////////////////////////////////////////////////// jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, - jbyteArray ninepatch) + jbyteArray ninepatch, int density) { SkASSERT(bitmap != NULL); SkASSERT(NULL != bitmap->pixelRef()); @@ -359,7 +359,7 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, jobject obj = env->AllocObject(gBitmap_class); if (obj) { env->CallVoidMethod(obj, gBitmap_constructorMethodID, - (jint)bitmap, isMutable, ninepatch); + (jint)bitmap, isMutable, ninepatch, density); if (hasException(env)) { obj = NULL; } @@ -541,7 +541,7 @@ int register_android_graphics_Graphics(JNIEnv* env) gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "", - "(IZ[B)V"); + "(IZ[BI)V"); gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config"); gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class, diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index f8b60a8..7adadbc 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -50,7 +50,7 @@ public: then the bitmap must be an owner of its natively allocated pixels (via allocPixels). */ static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, - jbyteArray ninePatch); + jbyteArray ninePatch, int density = -1); static jobject createRegion(JNIEnv* env, SkRegion* region); diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp index 0f8dff1..455449e 100644 --- a/core/jni/android/graphics/MaskFilter.cpp +++ b/core/jni/android/graphics/MaskFilter.cpp @@ -1,6 +1,7 @@ #include "GraphicsJNI.h" #include "SkMaskFilter.h" #include "SkBlurMaskFilter.h" +#include "SkTableMaskFilter.h" #include @@ -39,6 +40,19 @@ public: ThrowIAE_IfNull(env, filter); return filter; } + + static SkMaskFilter* createTable(JNIEnv* env, jobject, jbyteArray jtable) { + AutoJavaByteArray autoTable(env, jtable, 256); + return new SkTableMaskFilter((const uint8_t*)autoTable.ptr()); + } + + static SkMaskFilter* createClipTable(JNIEnv* env, jobject, int min, int max) { + return SkTableMaskFilter::CreateClip(min, max); + } + + static SkMaskFilter* createGammaTable(JNIEnv* env, jobject, float gamma) { + return SkTableMaskFilter::CreateGamma(gamma); + } }; static JNINativeMethod gMaskFilterMethods[] = { @@ -53,6 +67,12 @@ static JNINativeMethod gEmbossMaskFilterMethods[] = { { "nativeConstructor", "([FFFF)I", (void*)SkMaskFilterGlue::createEmboss } }; +static JNINativeMethod gTableMaskFilterMethods[] = { + { "nativeNewTable", "([B)I", (void*)SkMaskFilterGlue::createTable }, + { "nativeNewClip", "(II)I", (void*)SkMaskFilterGlue::createClipTable }, + { "nativeNewGamma", "(F)I", (void*)SkMaskFilterGlue::createGammaTable } +}; + #include #define REG(env, name, array) \ @@ -67,6 +87,7 @@ int register_android_graphics_MaskFilter(JNIEnv* env) REG(env, "android/graphics/MaskFilter", gMaskFilterMethods); REG(env, "android/graphics/BlurMaskFilter", gBlurMaskFilterMethods); REG(env, "android/graphics/EmbossMaskFilter", gEmbossMaskFilterMethods); + REG(env, "android/graphics/TableMaskFilter", gTableMaskFilterMethods); return 0; } diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index fd5271e..50df0f7 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -1,4 +1,25 @@ +/* +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "9patch" +#define LOG_NDEBUG 1 + #include +#include #include "SkCanvas.h" #include "SkRegion.h" @@ -62,6 +83,9 @@ public: if (destDensity == srcDensity || destDensity == 0 || srcDensity == 0) { + LOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)", + SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), + SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom)); NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); } else { canvas->save(); @@ -74,6 +98,11 @@ public: bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale); bounds.fLeft = bounds.fTop = 0; + LOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d", + SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), + SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom), + srcDensity, destDensity); + NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); canvas->restore(); diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp index f82053c..ff24a87 100644 --- a/core/jni/android/graphics/NinePatchImpl.cpp +++ b/core/jni/android/graphics/NinePatchImpl.cpp @@ -16,8 +16,10 @@ */ #define LOG_TAG "NinePatch" +#define LOG_NDEBUG 1 #include +#include #include "SkBitmap.h" #include "SkCanvas.h" @@ -25,7 +27,7 @@ #include "SkPaint.h" #include "SkUnPreMultiply.h" -#define USE_TRACEx +#define USE_TRACE #ifdef USE_TRACE static bool gTrace; @@ -106,6 +108,13 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, if (canvas && canvas->quickReject(bounds, SkCanvas::kBW_EdgeType)) { return; } + + SkPaint defaultPaint; + if (NULL == paint) { + // matches default dither in NinePatchDrawable.java. + defaultPaint.setDither(true); + paint = &defaultPaint; + } // if our canvas is GL, draw this as a mesh, which will be faster than // in parts (which is faster for raster) @@ -123,10 +132,10 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, SkASSERT(canvas || outRegion); -#if 0 +#ifdef USE_TRACE if (canvas) { const SkMatrix& m = canvas->getTotalMatrix(); - SkDebugf("ninepatch [%g %g %g] [%g %g %g]\n", + LOGV("ninepatch [%g %g %g] [%g %g %g]\n", SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]), SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5])); } @@ -134,10 +143,10 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, #ifdef USE_TRACE if (gTrace) { - SkDEBUGF(("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()))); - SkDEBUGF(("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height())); - SkDEBUGF(("======== ninepatch xDivs [%d,%d]\n", chunk.xDivs[0], chunk.xDivs[1])); - SkDEBUGF(("======== ninepatch yDivs [%d,%d]\n", chunk.yDivs[0], chunk.yDivs[1])); + LOGV("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height())); + LOGV("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height()); + LOGV("======== ninepatch xDivs [%d,%d]\n", chunk.xDivs[0], chunk.xDivs[1]); + LOGV("======== ninepatch yDivs [%d,%d]\n", chunk.yDivs[0], chunk.yDivs[1]); } #endif @@ -146,7 +155,7 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0)) { #ifdef USE_TRACE - if (gTrace) SkDEBUGF(("======== abort ninepatch draw\n")); + if (gTrace) LOGV("======== abort ninepatch draw\n"); #endif return; } @@ -158,11 +167,6 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, if (bitmap.getPixels() == NULL) return; - SkPaint defaultPaint; - if (NULL == paint) { - paint = &defaultPaint; - } - const bool hasXfer = paint->getXfermode() != NULL; SkRect dst; SkIRect src; @@ -196,8 +200,8 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, } int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining; -#if 0 - SkDebugf("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n", +#ifdef USE_TRACE + LOGV("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n", bitmap.width(), bitmap.height(), SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()), @@ -300,13 +304,13 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, goto nextDiv; } if (canvas) { -#if 0 - SkDebugf("-- src [%d %d %d %d] dst [%g %g %g %g]\n", +#ifdef USE_TRACE + LOGV("-- src [%d %d %d %d] dst [%g %g %g %g]\n", src.fLeft, src.fTop, src.width(), src.height(), SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop), SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height())); if (2 == src.width() && SkIntToScalar(5) == dst.width()) { - SkDebugf("--- skip patch\n"); + LOGV("--- skip patch\n"); } #endif drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor, diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index d1fe83e..780badc 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -48,6 +48,13 @@ static JMetricsID gFontMetrics_fieldID; static jclass gFontMetricsInt_class; static JMetricsID gFontMetricsInt_fieldID; +static void defaultSettingsForAndroid(SkPaint* paint) { + // looks best we decided + paint->setHinting(SkPaint::kSlight_Hinting); + // utf16 is required for java + paint->setTextEncoding(SkPaint::kUTF16_TextEncoding); +} + class SkPaintGlue { public: @@ -57,8 +64,7 @@ public: static SkPaint* init(JNIEnv* env, jobject clazz) { SkPaint* obj = new SkPaint(); - // utf16 is required for java - obj->setTextEncoding(SkPaint::kUTF16_TextEncoding); + defaultSettingsForAndroid(obj); return obj; } @@ -69,8 +75,7 @@ public: static void reset(JNIEnv* env, jobject clazz, SkPaint* obj) { obj->reset(); - // utf16 is required for java - obj->setTextEncoding(SkPaint::kUTF16_TextEncoding); + defaultSettingsForAndroid(obj); } static void assign(JNIEnv* env, jobject clazz, SkPaint* dst, const SkPaint* src) { @@ -564,11 +569,11 @@ static JNINativeMethod methods[] = { {"descent","()F", (void*) SkPaintGlue::descent}, {"getFontMetrics", "(Landroid/graphics/Paint$FontMetrics;)F", (void*)SkPaintGlue::getFontMetrics}, {"getFontMetricsInt", "(Landroid/graphics/Paint$FontMetricsInt;)I", (void*)SkPaintGlue::getFontMetricsInt}, - {"measureText","([CII)F", (void*) SkPaintGlue::measureText_CII}, - {"measureText","(Ljava/lang/String;)F", (void*) SkPaintGlue::measureText_String}, - {"measureText","(Ljava/lang/String;II)F", (void*) SkPaintGlue::measureText_StringII}, - {"breakText","([CIIF[F)I", (void*) SkPaintGlue::breakTextC}, - {"breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS}, + {"native_measureText","([CII)F", (void*) SkPaintGlue::measureText_CII}, + {"native_measureText","(Ljava/lang/String;)F", (void*) SkPaintGlue::measureText_String}, + {"native_measureText","(Ljava/lang/String;II)F", (void*) SkPaintGlue::measureText_StringII}, + {"native_breakText","([CIIF[F)I", (void*) SkPaintGlue::breakTextC}, + {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS}, {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F}, {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F}, {"native_getTextPath","(I[CIIFFI)V", (void*) SkPaintGlue::getTextPath___CIIFFPath}, diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp index effb1c8..11c608c 100644 --- a/core/jni/android/graphics/Path.cpp +++ b/core/jni/android/graphics/Path.cpp @@ -75,9 +75,8 @@ public: return result; } - static void computeBounds(JNIEnv* env, jobject clazz, SkPath* obj, jobject bounds, SkPath::BoundsType btype) { - SkRect bounds_; - obj->computeBounds(&bounds_, btype); + static void computeBounds(JNIEnv* env, jobject clazz, SkPath* obj, jobject bounds, int boundstype) { + const SkRect& bounds_ = obj->getBounds(); GraphicsJNI::rect_to_jrectf(bounds_, env, bounds); } diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp index 1dc0314..723cd37 100644 --- a/core/jni/android/graphics/Region.cpp +++ b/core/jni/android/graphics/Region.cpp @@ -134,7 +134,7 @@ static void Region_scale(JNIEnv* env, jobject region, jfloat scale, jobject dst) //////////////////////////////////////////////////////////////////////////////////////////////////////////// -#include "Parcel.h" +#include #include "android_util_Binder.h" static SkRegion* Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel) diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp index 21dde63..238ece1 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/core/jni/android/graphics/Typeface.cpp @@ -141,6 +141,25 @@ static SkTypeface* Typeface_createFromFile(JNIEnv* env, jobject, jstring jpath) return SkTypeface::CreateFromFile(str.c_str()); } +#define MIN_GAMMA (0.1f) +#define MAX_GAMMA (10.0f) +static float pinGamma(float gamma) { + if (gamma < MIN_GAMMA) { + gamma = MIN_GAMMA; + } else if (gamma > MAX_GAMMA) { + gamma = MAX_GAMMA; + } + return gamma; +} + +extern void skia_set_text_gamma(float, float); + +static void Typeface_setGammaForText(JNIEnv* env, jobject, jfloat blackGamma, + jfloat whiteGamma) { + // Comment this out for release builds. This is only used during development + skia_set_text_gamma(pinGamma(blackGamma), pinGamma(whiteGamma)); +} + /////////////////////////////////////////////////////////////////////////////// static JNINativeMethod gTypefaceMethods[] = { @@ -151,7 +170,8 @@ static JNINativeMethod gTypefaceMethods[] = { { "nativeCreateFromAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;)I", (void*)Typeface_createFromAsset }, { "nativeCreateFromFile", "(Ljava/lang/String;)I", - (void*)Typeface_createFromFile } + (void*)Typeface_createFromFile }, + { "setGammaForText", "(FF)V", (void*)Typeface_setGammaForText }, }; int register_android_graphics_Typeface(JNIEnv* env); diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp index 41044db..4041346 100644 --- a/core/jni/android/opengl/util.cpp +++ b/core/jni/android/opengl/util.cpp @@ -1,16 +1,16 @@ /** ** Copyright 2007, The Android Open Source Project ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at ** - ** http://www.apache.org/licenses/LICENSE-2.0 + ** http://www.apache.org/licenses/LICENSE-2.0 ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and ** limitations under the License. */ @@ -53,18 +53,18 @@ public: MallocHelper() { mData = 0; } - + ~MallocHelper() { if (mData != 0) { free(mData); } } - + void* alloc(size_t size) { mData = malloc(size); return mData; } - + private: void* mData; }; @@ -88,17 +88,17 @@ int visibilityTest(float* pWS, float* pPositions, int positionsLength, int result = POLY_CLIP_OUT; float* pTransformed = 0; int transformedIndexCount = 0; - + if ( indexCount < 3 ) { return POLY_CLIP_OUT; } - + // Find out how many vertices we need to transform // We transform every vertex between the min and max indices, inclusive. // This is OK for the data sets we expect to use with this function, but // for other loads it might be better to use a more sophisticated vertex // cache of some sort. - + int minIndex = 65536; int maxIndex = -1; for(int i = 0; i < indexCount; i++) { @@ -110,18 +110,18 @@ int visibilityTest(float* pWS, float* pPositions, int positionsLength, maxIndex = index; } } - + if ( maxIndex * 3 > positionsLength) { return -1; } - + transformedIndexCount = maxIndex - minIndex + 1; pTransformed = (float*) mallocHelper.alloc(transformedIndexCount * 4 * sizeof(float)); - + if (pTransformed == 0 ) { return -2; } - + // Transform the vertices { const float* pSrc = pPositions + 3 * minIndex; @@ -130,9 +130,9 @@ int visibilityTest(float* pWS, float* pPositions, int positionsLength, mx4transform(pSrc[0], pSrc[1], pSrc[2], 1.0f, pWS, pDst); } } - + // Clip the triangles - + Poly poly; float* pDest = & poly.vert[0].sx; for (int i = 0; i < indexCount; i += 3) { @@ -166,14 +166,14 @@ public: mEnv->ReleasePrimitiveArrayCritical(mRef, mBase, mReleaseParam); } } - + // We seperate the bounds check from the initialization because we want to // be able to bounds-check multiple arrays, and we can't throw an exception // after we've called GetPrimitiveArrayCritical. - + // Return true if the bounds check succeeded // Else instruct the runtime to throw an exception - + bool check() { if ( ! mRef) { mEnv->ThrowNew(gIAEClass, "array == null"); @@ -190,9 +190,9 @@ public: } return true; } - + // Bind the array. - + void bind() { mBase = (T*) mEnv->GetPrimitiveArrayCritical(mRef, (jboolean *) 0); mData = mBase + mOffset; @@ -201,10 +201,10 @@ public: void commitChanges() { mReleaseParam = 0; } - + T* mData; int mLength; - + private: T* mBase; JNIEnv* mEnv; @@ -226,29 +226,29 @@ inline float distance2(float x, float y, float z) { inline float distance(float x, float y, float z) { return sqrtf(distance2(x, y, z)); } - + static void util_computeBoundingSphere(JNIEnv *env, jclass clazz, jfloatArray positions_ref, jint positionsOffset, jint positionsCount, jfloatArray sphere_ref, jint sphereOffset) { FloatArrayHelper positions(env, positions_ref, positionsOffset, 0); FloatArrayHelper sphere(env, sphere_ref, sphereOffset, 4); - + bool checkOK = positions.check() && sphere.check(); if (! checkOK) { return; } - + positions.bind(); sphere.bind(); - + if ( positionsCount < 1 ) { env->ThrowNew(gIAEClass, "positionsCount < 1"); return; } - + const float* pSrc = positions.mData; - + // find bounding box float x0 = *pSrc++; float x1 = x0; @@ -256,7 +256,7 @@ void util_computeBoundingSphere(JNIEnv *env, jclass clazz, float y1 = y0; float z0 = *pSrc++; float z1 = z0; - + for(int i = 1; i < positionsCount; i++) { { float x = *pSrc++; @@ -286,7 +286,7 @@ void util_computeBoundingSphere(JNIEnv *env, jclass clazz, } } } - + // Because we know our input meshes fit pretty well into bounding boxes, // just take the diagonal of the box as defining our sphere. float* pSphere = sphere.mData; @@ -297,7 +297,7 @@ void util_computeBoundingSphere(JNIEnv *env, jclass clazz, *pSphere++ = y0 + dy * 0.5f; *pSphere++ = z0 + dz * 0.5f; *pSphere++ = distance(dx, dy, dz) * 0.5f; - + sphere.commitChanges(); } @@ -344,7 +344,7 @@ static void computeFrustum(const float* m, float* f) { normalizePlane(f); f+= 4; - // left + // left f[0] = m3 + m[0]; f[1] = m7 + m[4]; f[2] = m11 + m[8]; @@ -396,20 +396,20 @@ int util_frustumCullSpheres(JNIEnv *env, jclass clazz, FloatArrayHelper mvp(env, mvp_ref, mvpOffset, 16); FloatArrayHelper spheres(env, spheres_ref, spheresOffset, spheresCount * 4); IntArrayHelper results(env, results_ref, resultsOffset, resultsCapacity); - + bool initializedOK = mvp.check() && spheres.check() && results.check(); if (! initializedOK) { return -1; } - + mvp.bind(); spheres.bind(); results.bind(); - + computeFrustum(mvp.mData, frustum); - + // Cull the spheres - + pSphere = spheres.mData; pResults = results.mData; outputCount = 0; @@ -436,27 +436,27 @@ int util_visibilityTest(JNIEnv *env, jclass clazz, jfloatArray ws_ref, jint wsOffset, jfloatArray positions_ref, jint positionsOffset, jcharArray indices_ref, jint indicesOffset, jint indexCount) { - + FloatArrayHelper ws(env, ws_ref, wsOffset, 16); FloatArrayHelper positions(env, positions_ref, positionsOffset, 0); UnsignedShortArrayHelper indices(env, indices_ref, indicesOffset, 0); - + bool checkOK = ws.check() && positions.check() && indices.check(); if (! checkOK) { // Return value will be ignored, because an exception has been thrown. return -1; } - + if (indices.mLength < indexCount) { env->ThrowNew(gIAEClass, "length < offset + indexCount"); // Return value will be ignored, because an exception has been thrown. return -1; } - + ws.bind(); positions.bind(); indices.bind(); - + return visibilityTest(ws.mData, positions.mData, positions.mLength, indices.mData, indexCount); @@ -496,19 +496,19 @@ void util_multiplyMM(JNIEnv *env, jclass clazz, FloatArrayHelper resultMat(env, result_ref, resultOffset, 16); FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16); FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 16); - + bool checkOK = resultMat.check() && lhs.check() && rhs.check(); - + if ( !checkOK ) { return; } - + resultMat.bind(); lhs.bind(); rhs.bind(); - + multiplyMM(resultMat.mData, lhs.mData, rhs.mData); - + resultMat.commitChanges(); } @@ -527,19 +527,19 @@ void util_multiplyMV(JNIEnv *env, jclass clazz, FloatArrayHelper resultV(env, result_ref, resultOffset, 4); FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16); FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 4); - + bool checkOK = resultV.check() && lhs.check() && rhs.check(); - + if ( !checkOK ) { return; } - + resultV.bind(); lhs.bind(); rhs.bind(); - + multiplyMV(resultV.mData, lhs.mData, rhs.mData); - + resultV.commitChanges(); } @@ -550,7 +550,7 @@ static jfieldID nativeBitmapID = 0; void nativeUtilsClassInit(JNIEnv *env, jclass clazz) { jclass bitmapClass = env->FindClass("android/graphics/Bitmap"); - nativeBitmapID = env->GetFieldID(bitmapClass, "mNativeBitmap", "I"); + nativeBitmapID = env->GetFieldID(bitmapClass, "mNativeBitmap", "I"); } static int checkFormat(SkBitmap::Config config, int format, int type) @@ -653,7 +653,7 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz, } int err = checkFormat(config, internalformat, type); if (err) - return err; + return err; bitmap.lockPixels(); const int w = bitmap.width(); const int h = bitmap.height(); @@ -665,14 +665,15 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz, } const size_t size = bitmap.getSize(); const size_t palette_size = 256*sizeof(SkPMColor); - void* const data = malloc(size + palette_size); + const size_t imageSize = size + palette_size; + void* const data = malloc(imageSize); if (data) { void* const pixels = (char*)data + palette_size; SkColorTable* ctable = bitmap.getColorTable(); memcpy(data, ctable->lockColors(), ctable->count() * sizeof(SkPMColor)); memcpy(pixels, p, size); ctable->unlockColors(false); - glCompressedTexImage2D(target, level, internalformat, w, h, border, 0, data); + glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data); free(data); } else { err = -1; @@ -700,7 +701,7 @@ static jint util_texSubImage2D(JNIEnv *env, jclass clazz, } int err = checkFormat(config, format, type); if (err) - return err; + return err; bitmap.lockPixels(); const int w = bitmap.width(); const int h = bitmap.height(); @@ -723,22 +724,22 @@ lookupClasses(JNIEnv* env) { } static JNINativeMethod gMatrixMethods[] = { - { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM }, - { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV }, + { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM }, + { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV }, }; static JNINativeMethod gVisiblityMethods[] = { - { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere }, + { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere }, { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres }, - { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest }, + { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest }, }; static JNINativeMethod gUtilsMethods[] = { {"nativeClassInit", "()V", (void*)nativeUtilsClassInit }, - { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat }, - { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType }, - { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D }, - { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D }, + { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat }, + { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType }, + { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D }, + { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D }, }; typedef struct _ClassRegistrationInfo { diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp new file mode 100644 index 0000000..31ebf8c --- /dev/null +++ b/core/jni/android_bluetooth_BluetoothSocket.cpp @@ -0,0 +1,557 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BluetoothSocket.cpp" + +#include "android_bluetooth_common.h" +#include "android_runtime/AndroidRuntime.h" +#include "JNIHelp.h" +#include "utils/Log.h" +#include "cutils/abort_socket.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_BLUETOOTH +#include +#include +#include +#include +#endif + +#define TYPE_AS_STR(t) \ + ((t) == TYPE_RFCOMM ? "RFCOMM" : ((t) == TYPE_SCO ? "SCO" : "L2CAP")) + +namespace android { + +static jfieldID field_mAuth; /* read-only */ +static jfieldID field_mEncrypt; /* read-only */ +static jfieldID field_mType; /* read-only */ +static jfieldID field_mAddress; /* read-only */ +static jfieldID field_mPort; /* read-only */ +static jfieldID field_mSocketData; +static jmethodID method_BluetoothSocket_ctor; +static jclass class_BluetoothSocket; + +/* Keep TYPE_RFCOMM etc in sync with BluetoothSocket.java */ +static const int TYPE_RFCOMM = 1; +static const int TYPE_SCO = 2; +static const int TYPE_L2CAP = 3; // TODO: Test l2cap code paths + +static const int RFCOMM_SO_SNDBUF = 70 * 1024; // 70 KB send buffer + +static struct asocket *get_socketData(JNIEnv *env, jobject obj) { + struct asocket *s = + (struct asocket *) env->GetIntField(obj, field_mSocketData); + if (!s) + jniThrowException(env, "java/io/IOException", "null socketData"); + return s; +} + +static void initSocketFromFdNative(JNIEnv *env, jobject obj, jint fd) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + + struct asocket *s = asocket_init(fd); + + if (!s) { + LOGV("asocket_init() failed, throwing"); + jniThrowIOException(env, errno); + return; + } + + env->SetIntField(obj, field_mSocketData, (jint)s); + + return; +#endif + jniThrowIOException(env, ENOSYS); +} + +static void initSocketNative(JNIEnv *env, jobject obj) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + + int fd; + int lm = 0; + int sndbuf; + jboolean auth; + jboolean encrypt; + jint type; + + type = env->GetIntField(obj, field_mType); + + switch (type) { + case TYPE_RFCOMM: + fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + break; + case TYPE_SCO: + fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); + break; + case TYPE_L2CAP: + fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + break; + default: + jniThrowIOException(env, ENOSYS); + return; + } + + if (fd < 0) { + LOGV("socket() failed, throwing"); + jniThrowIOException(env, errno); + return; + } + + auth = env->GetBooleanField(obj, field_mAuth); + encrypt = env->GetBooleanField(obj, field_mEncrypt); + + /* kernel does not yet support LM for SCO */ + switch (type) { + case TYPE_RFCOMM: + lm |= auth ? RFCOMM_LM_AUTH : 0; + lm |= encrypt ? RFCOMM_LM_ENCRYPT : 0; + lm |= (auth && encrypt) ? RFCOMM_LM_SECURE : 0; + break; + case TYPE_L2CAP: + lm |= auth ? L2CAP_LM_AUTH : 0; + lm |= encrypt ? L2CAP_LM_ENCRYPT : 0; + lm |= (auth && encrypt) ? L2CAP_LM_SECURE : 0; + break; + } + + if (lm) { + if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) { + LOGV("setsockopt(RFCOMM_LM) failed, throwing"); + jniThrowIOException(env, errno); + return; + } + } + + if (type == TYPE_RFCOMM) { + sndbuf = RFCOMM_SO_SNDBUF; + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) { + LOGV("setsockopt(SO_SNDBUF) failed, throwing"); + jniThrowIOException(env, errno); + return; + } + } + + LOGV("...fd %d created (%s, lm = %x)", fd, TYPE_AS_STR(type), lm); + + initSocketFromFdNative(env, obj, fd); + return; +#endif + jniThrowIOException(env, ENOSYS); +} + +static void connectNative(JNIEnv *env, jobject obj) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + + int ret; + jint type; + const char *c_address; + jstring address; + bdaddr_t bdaddress; + socklen_t addr_sz; + struct sockaddr *addr; + struct asocket *s = get_socketData(env, obj); + + if (!s) + return; + + type = env->GetIntField(obj, field_mType); + + /* parse address into bdaddress */ + address = (jstring) env->GetObjectField(obj, field_mAddress); + c_address = env->GetStringUTFChars(address, NULL); + if (get_bdaddr(c_address, &bdaddress)) { + env->ReleaseStringUTFChars(address, c_address); + jniThrowIOException(env, EINVAL); + return; + } + env->ReleaseStringUTFChars(address, c_address); + + switch (type) { + case TYPE_RFCOMM: + struct sockaddr_rc addr_rc; + addr = (struct sockaddr *)&addr_rc; + addr_sz = sizeof(addr_rc); + + memset(addr, 0, addr_sz); + addr_rc.rc_family = AF_BLUETOOTH; + addr_rc.rc_channel = env->GetIntField(obj, field_mPort); + memcpy(&addr_rc.rc_bdaddr, &bdaddress, sizeof(bdaddr_t)); + + break; + case TYPE_SCO: + struct sockaddr_sco addr_sco; + addr = (struct sockaddr *)&addr_sco; + addr_sz = sizeof(addr_sco); + + memset(addr, 0, addr_sz); + addr_sco.sco_family = AF_BLUETOOTH; + memcpy(&addr_sco.sco_bdaddr, &bdaddress, sizeof(bdaddr_t)); + + break; + case TYPE_L2CAP: + struct sockaddr_l2 addr_l2; + addr = (struct sockaddr *)&addr_l2; + addr_sz = sizeof(addr_l2); + + memset(addr, 0, addr_sz); + addr_l2.l2_family = AF_BLUETOOTH; + addr_l2.l2_psm = env->GetIntField(obj, field_mPort); + memcpy(&addr_l2.l2_bdaddr, &bdaddress, sizeof(bdaddr_t)); + + break; + default: + jniThrowIOException(env, ENOSYS); + return; + } + + ret = asocket_connect(s, addr, addr_sz, -1); + LOGV("...connect(%d, %s) = %d (errno %d)", + s->fd, TYPE_AS_STR(type), ret, errno); + + if (ret) + jniThrowIOException(env, errno); + + return; +#endif + jniThrowIOException(env, ENOSYS); +} + +/* Returns errno instead of throwing, so java can check errno */ +static int bindListenNative(JNIEnv *env, jobject obj) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + + jint type; + socklen_t addr_sz; + struct sockaddr *addr; + bdaddr_t bdaddr = *BDADDR_ANY; + struct asocket *s = get_socketData(env, obj); + + if (!s) + return EINVAL; + + type = env->GetIntField(obj, field_mType); + + switch (type) { + case TYPE_RFCOMM: + struct sockaddr_rc addr_rc; + addr = (struct sockaddr *)&addr_rc; + addr_sz = sizeof(addr_rc); + + memset(addr, 0, addr_sz); + addr_rc.rc_family = AF_BLUETOOTH; + addr_rc.rc_channel = env->GetIntField(obj, field_mPort); + memcpy(&addr_rc.rc_bdaddr, &bdaddr, sizeof(bdaddr_t)); + break; + case TYPE_SCO: + struct sockaddr_sco addr_sco; + addr = (struct sockaddr *)&addr_sco; + addr_sz = sizeof(addr_sco); + + memset(addr, 0, addr_sz); + addr_sco.sco_family = AF_BLUETOOTH; + memcpy(&addr_sco.sco_bdaddr, &bdaddr, sizeof(bdaddr_t)); + break; + case TYPE_L2CAP: + struct sockaddr_l2 addr_l2; + addr = (struct sockaddr *)&addr_l2; + addr_sz = sizeof(addr_l2); + + memset(addr, 0, addr_sz); + addr_l2.l2_family = AF_BLUETOOTH; + addr_l2.l2_psm = env->GetIntField(obj, field_mPort); + memcpy(&addr_l2.l2_bdaddr, &bdaddr, sizeof(bdaddr_t)); + break; + default: + return ENOSYS; + } + + if (bind(s->fd, addr, addr_sz)) { + LOGV("...bind(%d) gave errno %d", s->fd, errno); + return errno; + } + + if (listen(s->fd, 1)) { + LOGV("...listen(%d) gave errno %d", s->fd, errno); + return errno; + } + + LOGV("...bindListenNative(%d) success", s->fd); + + return 0; + +#endif + return ENOSYS; +} + +static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + + int fd; + jint type; + struct sockaddr *addr; + socklen_t addr_sz; + jstring addr_jstr; + char addr_cstr[BTADDR_SIZE]; + bdaddr_t *bdaddr; + jboolean auth; + jboolean encrypt; + + struct asocket *s = get_socketData(env, obj); + + if (!s) + return NULL; + + type = env->GetIntField(obj, field_mType); + + switch (type) { + case TYPE_RFCOMM: + struct sockaddr_rc addr_rc; + addr = (struct sockaddr *)&addr_rc; + addr_sz = sizeof(addr_rc); + bdaddr = &addr_rc.rc_bdaddr; + memset(addr, 0, addr_sz); + break; + case TYPE_SCO: + struct sockaddr_sco addr_sco; + addr = (struct sockaddr *)&addr_sco; + addr_sz = sizeof(addr_sco); + bdaddr = &addr_sco.sco_bdaddr; + memset(addr, 0, addr_sz); + break; + case TYPE_L2CAP: + struct sockaddr_l2 addr_l2; + addr = (struct sockaddr *)&addr_l2; + addr_sz = sizeof(addr_l2); + bdaddr = &addr_l2.l2_bdaddr; + memset(addr, 0, addr_sz); + break; + default: + jniThrowIOException(env, ENOSYS); + return NULL; + } + + fd = asocket_accept(s, addr, &addr_sz, timeout); + + LOGV("...accept(%d, %s) = %d (errno %d)", + s->fd, TYPE_AS_STR(type), fd, errno); + + if (fd < 0) { + jniThrowIOException(env, errno); + return NULL; + } + + /* Connected - return new BluetoothSocket */ + auth = env->GetBooleanField(obj, field_mAuth); + encrypt = env->GetBooleanField(obj, field_mEncrypt); + + get_bdaddr_as_string(bdaddr, addr_cstr); + + addr_jstr = env->NewStringUTF(addr_cstr); + return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor, + type, fd, auth, encrypt, addr_jstr, -1); + +#endif + jniThrowIOException(env, ENOSYS); + return NULL; +} + +static jint availableNative(JNIEnv *env, jobject obj) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + + int available; + struct asocket *s = get_socketData(env, obj); + + if (!s) + return -1; + + if (ioctl(s->fd, FIONREAD, &available) < 0) { + jniThrowIOException(env, errno); + return -1; + } + + return available; + +#endif + jniThrowIOException(env, ENOSYS); + return -1; +} + +static jint readNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset, + jint length) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + + int ret; + jbyte *b; + int sz; + struct asocket *s = get_socketData(env, obj); + + if (!s) + return -1; + if (jb == NULL) { + jniThrowIOException(env, EINVAL); + return -1; + } + sz = env->GetArrayLength(jb); + if (offset < 0 || length < 0 || offset + length > sz) { + jniThrowIOException(env, EINVAL); + return -1; + } + + b = env->GetByteArrayElements(jb, NULL); + if (b == NULL) { + jniThrowIOException(env, EINVAL); + return -1; + } + + ret = asocket_read(s, &b[offset], length, -1); + if (ret < 0) { + jniThrowIOException(env, errno); + env->ReleaseByteArrayElements(jb, b, JNI_ABORT); + return -1; + } + + env->ReleaseByteArrayElements(jb, b, 0); + return (jint)ret; + +#endif + jniThrowIOException(env, ENOSYS); + return -1; +} + +static jint writeNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset, + jint length) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + + int ret; + jbyte *b; + int sz; + struct asocket *s = get_socketData(env, obj); + + if (!s) + return -1; + if (jb == NULL) { + jniThrowIOException(env, EINVAL); + return -1; + } + sz = env->GetArrayLength(jb); + if (offset < 0 || length < 0 || offset + length > sz) { + jniThrowIOException(env, EINVAL); + return -1; + } + + b = env->GetByteArrayElements(jb, NULL); + if (b == NULL) { + jniThrowIOException(env, EINVAL); + return -1; + } + + ret = asocket_write(s, &b[offset], length, -1); + if (ret < 0) { + jniThrowIOException(env, errno); + env->ReleaseByteArrayElements(jb, b, JNI_ABORT); + return -1; + } + + env->ReleaseByteArrayElements(jb, b, JNI_ABORT); // no need to commit + return (jint)ret; + +#endif + jniThrowIOException(env, ENOSYS); + return -1; +} + +static void abortNative(JNIEnv *env, jobject obj) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + struct asocket *s = get_socketData(env, obj); + + if (!s) + return; + + asocket_abort(s); + + LOGV("...asocket_abort(%d) complete", s->fd); + return; +#endif + jniThrowIOException(env, ENOSYS); +} + +static void destroyNative(JNIEnv *env, jobject obj) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + struct asocket *s = get_socketData(env, obj); + int fd = s->fd; + + if (!s) + return; + + asocket_destroy(s); + + LOGV("...asocket_destroy(%d) complete", fd); + return; +#endif + jniThrowIOException(env, ENOSYS); +} + +static void throwErrnoNative(JNIEnv *env, jobject obj, jint err) { + jniThrowIOException(env, err); +} + +static JNINativeMethod sMethods[] = { + {"initSocketNative", "()V", (void*) initSocketNative}, + {"initSocketFromFdNative", "(I)V", (void*) initSocketFromFdNative}, + {"connectNative", "()V", (void *) connectNative}, + {"bindListenNative", "()I", (void *) bindListenNative}, + {"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative}, + {"availableNative", "()I", (void *) availableNative}, + {"readNative", "([BII)I", (void *) readNative}, + {"writeNative", "([BII)I", (void *) writeNative}, + {"abortNative", "()V", (void *) abortNative}, + {"destroyNative", "()V", (void *) destroyNative}, + {"throwErrnoNative", "(I)V", (void *) throwErrnoNative}, +}; + +int register_android_bluetooth_BluetoothSocket(JNIEnv *env) { + jclass clazz = env->FindClass("android/bluetooth/BluetoothSocket"); + if (clazz == NULL) + return -1; + class_BluetoothSocket = (jclass) env->NewGlobalRef(clazz); + field_mType = env->GetFieldID(clazz, "mType", "I"); + field_mAddress = env->GetFieldID(clazz, "mAddress", "Ljava/lang/String;"); + field_mPort = env->GetFieldID(clazz, "mPort", "I"); + field_mAuth = env->GetFieldID(clazz, "mAuth", "Z"); + field_mEncrypt = env->GetFieldID(clazz, "mEncrypt", "Z"); + field_mSocketData = env->GetFieldID(clazz, "mSocketData", "I"); + method_BluetoothSocket_ctor = env->GetMethodID(clazz, "", "(IIZZLjava/lang/String;I)V"); + return AndroidRuntime::registerNativeMethods(env, + "android/bluetooth/BluetoothSocket", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ + diff --git a/core/jni/android_bluetooth_Database.cpp b/core/jni/android_bluetooth_Database.cpp deleted file mode 100644 index 73b8efd..0000000 --- a/core/jni/android_bluetooth_Database.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Database" -#define LOG_TAG "bluetooth_Database.cpp" - -#include "android_bluetooth_common.h" -#include "android_runtime/AndroidRuntime.h" -#include "JNIHelp.h" -#include "jni.h" -#include "utils/Log.h" - -#ifdef HAVE_BLUETOOTH -#include -#endif - -namespace android { - -#ifdef HAVE_BLUETOOTH -static DBusConnection* conn = NULL; // Singleton thread-safe connection -#endif - -static void classInitNative(JNIEnv* env, jclass clazz) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - conn = NULL; -#endif -} - -static void initializeNativeDataNative(JNIEnv* env, jobject object) { - LOGV(__FUNCTION__); - -#ifdef HAVE_BLUETOOTH - if (conn == NULL) { - DBusError err; - dbus_error_init(&err); - dbus_threads_init_default(); - conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); - if (dbus_error_is_set(&err)) { - LOGE("Could not get onto the system bus!"); - dbus_error_free(&err); - } - dbus_connection_set_exit_on_disconnect(conn, FALSE); - } -#endif -} - -static void cleanupNativeDataNative(JNIEnv* env, jobject object) { - LOGV(__FUNCTION__); -} - -static jint addServiceRecordNative(JNIEnv *env, jobject object, - jbyteArray record) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - if (conn != NULL) { - jbyte* c_record = env->GetByteArrayElements(record, NULL); - DBusMessage *reply = dbus_func_args(env, - conn, - BLUEZ_DBUS_BASE_PATH, - DBUS_CLASS_NAME, - "AddServiceRecord", - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &c_record, - env->GetArrayLength(record), - DBUS_TYPE_INVALID); - env->ReleaseByteArrayElements(record, c_record, JNI_ABORT); - return reply ? dbus_returns_uint32(env, reply) : -1; - } -#endif - return -1; -} - -static jint addServiceRecordFromXmlNative(JNIEnv *env, jobject object, - jstring record) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - if (conn != NULL) { - const char *c_record = env->GetStringUTFChars(record, NULL); - DBusMessage *reply = dbus_func_args(env, - conn, - BLUEZ_DBUS_BASE_PATH, - DBUS_CLASS_NAME, - "AddServiceRecordFromXML", - DBUS_TYPE_STRING, &c_record, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(record, c_record); - return reply ? dbus_returns_uint32(env, reply) : -1; - } -#endif - return -1; -} - -static void updateServiceRecordNative(JNIEnv *env, jobject object, - jint handle, - jbyteArray record) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - if (conn != NULL) { - jbyte* c_record = env->GetByteArrayElements(record, NULL); - DBusMessage *reply = dbus_func_args(env, - conn, - BLUEZ_DBUS_BASE_PATH, - DBUS_CLASS_NAME, - "UpdateServiceRecord", - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &c_record, - env->GetArrayLength(record), - DBUS_TYPE_INVALID); - env->ReleaseByteArrayElements(record, c_record, JNI_ABORT); - } -#endif -} - -static void updateServiceRecordFromXmlNative(JNIEnv *env, jobject object, - jint handle, - jstring record) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - if (conn != NULL) { - const char *c_record = env->GetStringUTFChars(record, NULL); - DBusMessage *reply = dbus_func_args(env, - conn, - BLUEZ_DBUS_BASE_PATH, - DBUS_CLASS_NAME, - "UpdateServiceRecordFromXML", - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_STRING, &c_record, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(record, c_record); - } -#endif -} - -/* private static native void removeServiceRecordNative(int handle); */ -static void removeServiceRecordNative(JNIEnv *env, jobject object, - jint handle) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - if (conn != NULL) { - DBusMessage *reply = dbus_func_args(env, - conn, - BLUEZ_DBUS_BASE_PATH, - DBUS_CLASS_NAME, - "RemoveServiceRecord", - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID); - } -#endif -} - - -static JNINativeMethod sMethods[] = { - /* name, signature, funcPtr */ - {"classInitNative", "()V", (void*)classInitNative}, - {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative}, - {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, - {"addServiceRecordNative", "([B)I", (void*)addServiceRecordNative}, - {"addServiceRecordFromXmlNative", "(Ljava/lang/String;)I", (void*)addServiceRecordFromXmlNative}, - {"updateServiceRecordNative", "(I[B)V", (void*)updateServiceRecordNative}, - {"updateServiceRecordFromXmlNative", "(ILjava/lang/String;)V", (void*)updateServiceRecordFromXmlNative}, - {"removeServiceRecordNative", "(I)V", (void*)removeServiceRecordNative}, -}; - -int register_android_bluetooth_Database(JNIEnv *env) { - return AndroidRuntime::registerNativeMethods(env, - "android/bluetooth/Database", sMethods, NELEM(sMethods)); -} - -} /* namespace android */ diff --git a/core/jni/android_bluetooth_HeadsetBase.cpp b/core/jni/android_bluetooth_HeadsetBase.cpp index bb19e92..71279b2 100644 --- a/core/jni/android_bluetooth_HeadsetBase.cpp +++ b/core/jni/android_bluetooth_HeadsetBase.cpp @@ -260,7 +260,7 @@ static jboolean connectNative(JNIEnv *env, jobject obj) #endif } -static jboolean connectAsyncNative(JNIEnv *env, jobject obj) { +static jint connectAsyncNative(JNIEnv *env, jobject obj) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH struct sockaddr_rc addr; @@ -268,7 +268,7 @@ static jboolean connectAsyncNative(JNIEnv *env, jobject obj) { if (nat->rfcomm_connected) { LOGV("RFCOMM socket is already connected or connection is in progress."); - return JNI_TRUE; + return 0; } if (nat->rfcomm_sock < 0) { @@ -278,7 +278,7 @@ static jboolean connectAsyncNative(JNIEnv *env, jobject obj) { if (nat->rfcomm_sock < 0) { LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__, strerror(errno)); - return JNI_FALSE; + return -1; } if (debug_no_encrypt()) { @@ -291,7 +291,7 @@ static jboolean connectAsyncNative(JNIEnv *env, jobject obj) { sizeof(lm)) < 0) { LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__); close(nat->rfcomm_sock); - return JNI_FALSE; + return -1; } LOGI("Created RFCOMM socket fd %d.", nat->rfcomm_sock); } @@ -314,7 +314,7 @@ static jboolean connectAsyncNative(JNIEnv *env, jobject obj) { if (rc >= 0) { nat->rfcomm_connected = 1; LOGI("async connect successful"); - return JNI_TRUE; + return 0; } else if (rc < 0) { if (errno == EINPROGRESS || errno == EAGAIN) @@ -322,20 +322,20 @@ static jboolean connectAsyncNative(JNIEnv *env, jobject obj) { LOGI("async connect is in progress (%s)", strerror(errno)); nat->rfcomm_connected = -1; - return JNI_TRUE; + return 0; } else { LOGE("async connect error: %s (%d)", strerror(errno), errno); close(nat->rfcomm_sock); nat->rfcomm_sock = -1; - return JNI_FALSE; + return -errno; } } } // fcntl(nat->rfcomm_sock ...) } // if (nat->rfcomm_sock_flags >= 0) #endif - return JNI_FALSE; + return -1; } static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj, @@ -357,9 +357,11 @@ static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj, close(nat->rfcomm_sock); nat->rfcomm_sock = -1; } - if (JNI_FALSE == connectAsyncNative(env, obj)) { + int ret = connectAsyncNative(env, obj); + + if (ret < 0) { LOGI("Failed to re-open RFCOMM socket!"); - return -1; + return ret; } if (nat->rfcomm_sock >= 0) { @@ -394,7 +396,7 @@ static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj, LOGE("select() on RFCOMM socket: %s (%d)", strerror(errno), errno); - return -1; + return -errno; } return 0; } @@ -427,7 +429,7 @@ static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj, fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags); close(nat->rfcomm_sock); nat->rfcomm_sock = -1; - return -1; + return -errno; } } /* Restore the blocking properties of the socket. */ @@ -532,7 +534,7 @@ static JNINativeMethod sMethods[] = { {"initializeNativeDataNative", "(I)V", (void *)initializeNativeDataNative}, {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, {"connectNative", "()Z", (void *)connectNative}, - {"connectAsyncNative", "()Z", (void *)connectAsyncNative}, + {"connectAsyncNative", "()I", (void *)connectAsyncNative}, {"waitForAsyncConnectNative", "(I)I", (void *)waitForAsyncConnectNative}, {"disconnectNative", "()V", (void *)disconnectNative}, {"sendURCNative", "(Ljava/lang/String;)Z", (void *)sendURCNative}, diff --git a/core/jni/android_bluetooth_RfcommSocket.cpp b/core/jni/android_bluetooth_RfcommSocket.cpp deleted file mode 100644 index 3ed35d9..0000000 --- a/core/jni/android_bluetooth_RfcommSocket.cpp +++ /dev/null @@ -1,621 +0,0 @@ -/* -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define LOG_TAG "bluetooth_RfcommSocket.cpp" - -#include "android_bluetooth_common.h" -#include "android_runtime/AndroidRuntime.h" -#include "JNIHelp.h" -#include "jni.h" -#include "utils/Log.h" -#include "utils/misc.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_BLUETOOTH -#include -#include -#include -#endif - -namespace android { - -#ifdef HAVE_BLUETOOTH -static jfieldID field_mNativeData; -static jfieldID field_mTimeoutRemainingMs; -static jfieldID field_mAcceptTimeoutRemainingMs; -static jfieldID field_mAddress; -static jfieldID field_mPort; - -typedef struct { - jstring address; - const char *c_address; - int rfcomm_channel; - int last_read_err; - int rfcomm_sock; - // < 0 -- in progress, - // 0 -- not connected - // > 0 connected - // 1 input is open - // 2 output is open - // 3 both input and output are open - int rfcomm_connected; - int rfcomm_sock_flags; -} native_data_t; - -static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { - return (native_data_t *)(env->GetIntField(object, field_mNativeData)); -} - -static inline void init_socket_info( - JNIEnv *env, jobject object, - native_data_t *nat, - jstring address, - jint rfcomm_channel) { - nat->address = (jstring)env->NewGlobalRef(address); - nat->c_address = env->GetStringUTFChars(nat->address, NULL); - nat->rfcomm_channel = (int)rfcomm_channel; -} - -static inline void cleanup_socket_info(JNIEnv *env, native_data_t *nat) { - if (nat->c_address != NULL) { - env->ReleaseStringUTFChars(nat->address, nat->c_address); - env->DeleteGlobalRef(nat->address); - nat->c_address = NULL; - } -} -#endif - -static void classInitNative(JNIEnv* env, jclass clazz) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - field_mNativeData = get_field(env, clazz, "mNativeData", "I"); - field_mTimeoutRemainingMs = get_field(env, clazz, "mTimeoutRemainingMs", "I"); - field_mAcceptTimeoutRemainingMs = get_field(env, clazz, "mAcceptTimeoutRemainingMs", "I"); - field_mAddress = get_field(env, clazz, "mAddress", "Ljava/lang/String;"); - field_mPort = get_field(env, clazz, "mPort", "I"); -#endif -} - -static void initializeNativeDataNative(JNIEnv* env, jobject object) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - - native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t)); - if (nat == NULL) { - LOGE("%s: out of memory!", __FUNCTION__); - return; - } - - env->SetIntField(object, field_mNativeData, (jint)nat); - nat->rfcomm_sock = -1; - nat->rfcomm_connected = 0; -#endif -} - -static void cleanupNativeDataNative(JNIEnv* env, jobject object) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); - if (nat) { - free(nat); - } -#endif -} - -static jobject createNative(JNIEnv *env, jobject obj) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - int lm; - native_data_t *nat = get_native_data(env, obj); - nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - - if (nat->rfcomm_sock < 0) { - LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__, - strerror(errno)); - return NULL; - } - - lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; - - if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm, - sizeof(lm)) < 0) { - LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__); - close(nat->rfcomm_sock); - return NULL; - } - - return jniCreateFileDescriptor(env, nat->rfcomm_sock); -#else - return NULL; -#endif -} - -static void destroyNative(JNIEnv *env, jobject obj) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, obj); - cleanup_socket_info(env, nat); - if (nat->rfcomm_sock >= 0) { - close(nat->rfcomm_sock); - nat->rfcomm_sock = -1; - } -#endif -} - - -static jboolean connectNative(JNIEnv *env, jobject obj, - jstring address, jint port) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, obj); - - if (nat->rfcomm_sock >= 0) { - if (nat->rfcomm_connected) { - LOGI("RFCOMM socket: %s.", - (nat->rfcomm_connected > 0) ? "already connected" : "connection is in progress"); - return JNI_TRUE; - } - - init_socket_info(env, obj, nat, address, port); - - struct sockaddr_rc addr; - memset(&addr, 0, sizeof(struct sockaddr_rc)); - get_bdaddr(nat->c_address, &addr.rc_bdaddr); - addr.rc_channel = nat->rfcomm_channel; - addr.rc_family = AF_BLUETOOTH; - nat->rfcomm_connected = 0; - - while (nat->rfcomm_connected == 0) { - if (connect(nat->rfcomm_sock, (struct sockaddr *)&addr, - sizeof(addr)) < 0) { - if (errno == EINTR) continue; - LOGE("connect error: %s (%d)\n", strerror(errno), errno); - break; - } else { - nat->rfcomm_connected = 3; // input and output - } - } - } else { - LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__); - } - - if (nat->rfcomm_connected > 0) { - env->SetIntField(obj, field_mPort, port); - return JNI_TRUE; - } -#endif - return JNI_FALSE; -} - -static jboolean connectAsyncNative(JNIEnv *env, jobject obj, - jstring address, jint port) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, obj); - - if (nat->rfcomm_sock < 0) { - LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__); - return JNI_FALSE; - } - - if (nat->rfcomm_connected) { - LOGI("RFCOMM socket: %s.", - (nat->rfcomm_connected > 0) ? - "already connected" : "connection is in progress"); - return JNI_TRUE; - } - - init_socket_info(env, obj, nat, address, port); - - struct sockaddr_rc addr; - memset(&addr, 0, sizeof(struct sockaddr_rc)); - get_bdaddr(nat->c_address, &addr.rc_bdaddr); - addr.rc_channel = nat->rfcomm_channel; - addr.rc_family = AF_BLUETOOTH; - - nat->rfcomm_sock_flags = fcntl(nat->rfcomm_sock, F_GETFL, 0); - if (fcntl(nat->rfcomm_sock, - F_SETFL, nat->rfcomm_sock_flags | O_NONBLOCK) >= 0) { - int rc; - nat->rfcomm_connected = 0; - errno = 0; - rc = connect(nat->rfcomm_sock, - (struct sockaddr *)&addr, - sizeof(addr)); - - if (rc >= 0) { - nat->rfcomm_connected = 3; - LOGI("RFCOMM async connect immediately successful"); - env->SetIntField(obj, field_mPort, port); - return JNI_TRUE; - } - else if (rc < 0) { - if (errno == EINPROGRESS || errno == EAGAIN) - { - LOGI("RFCOMM async connect is in progress (%s)", - strerror(errno)); - nat->rfcomm_connected = -1; - env->SetIntField(obj, field_mPort, port); - return JNI_TRUE; - } - else - { - LOGE("RFCOMM async connect error (%d): %s (%d)", - nat->rfcomm_sock, strerror(errno), errno); - return JNI_FALSE; - } - } - } // fcntl(nat->rfcomm_sock ...) -#endif - return JNI_FALSE; -} - -static jboolean interruptAsyncConnectNative(JNIEnv *env, jobject obj) { - //WRITEME - return JNI_TRUE; -} - -static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj, - jint timeout_ms) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - struct sockaddr_rc addr; - native_data_t *nat = get_native_data(env, obj); - - env->SetIntField(obj, field_mTimeoutRemainingMs, timeout_ms); - - if (nat->rfcomm_sock < 0) { - LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__); - return -1; - } - - if (nat->rfcomm_connected > 0) { - LOGI("%s: RFCOMM is already connected!", __FUNCTION__); - return 1; - } - - /* Do an asynchronous select() */ - int n; - fd_set rset, wset; - struct timeval to; - - FD_ZERO(&rset); - FD_ZERO(&wset); - FD_SET(nat->rfcomm_sock, &rset); - FD_SET(nat->rfcomm_sock, &wset); - if (timeout_ms >= 0) { - to.tv_sec = timeout_ms / 1000; - to.tv_usec = 1000 * (timeout_ms % 1000); - } - n = select(nat->rfcomm_sock + 1, - &rset, - &wset, - NULL, - (timeout_ms < 0 ? NULL : &to)); - - if (timeout_ms > 0) { - jint remaining = to.tv_sec*1000 + to.tv_usec/1000; - LOGI("Remaining time %ldms", (long)remaining); - env->SetIntField(obj, field_mTimeoutRemainingMs, - remaining); - } - - if (n <= 0) { - if (n < 0) { - LOGE("select() on RFCOMM socket: %s (%d)", - strerror(errno), - errno); - return -1; - } - return 0; - } - /* n must be equal to 1 and either rset or wset must have the - file descriptor set. */ - LOGI("select() returned %d.", n); - if (FD_ISSET(nat->rfcomm_sock, &rset) || - FD_ISSET(nat->rfcomm_sock, &wset)) { - /* A trial async read() will tell us if everything is OK. */ - char ch; - errno = 0; - int nr = read(nat->rfcomm_sock, &ch, 1); - /* It should be that nr != 1 because we just opened a socket - and we haven't sent anything over it for the other side to - respond... but one can't be paranoid enough. - */ - if (nr >= 0 || errno != EAGAIN) { - LOGE("RFCOMM async connect() error: %s (%d), nr = %d\n", - strerror(errno), - errno, - nr); - /* Clear the rfcomm_connected flag to cause this function - to re-create the socket and re-attempt the connect() - the next time it is called. - */ - nat->rfcomm_connected = 0; - /* Restore the blocking properties of the socket. */ - fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags); - return -1; - } - /* Restore the blocking properties of the socket. */ - fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags); - LOGI("Successful RFCOMM socket connect."); - nat->rfcomm_connected = 3; // input and output - return 1; - } -#endif - return -1; -} - -static jboolean shutdownNative(JNIEnv *env, jobject obj, - jboolean shutdownInput) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - /* NOTE: If you change the bcode to modify nat, make sure you - add synchronize(this) to the method calling this native - method. - */ - native_data_t *nat = get_native_data(env, obj); - if (nat->rfcomm_sock < 0) { - LOGE("socket(RFCOMM) error: socket not created"); - return JNI_FALSE; - } - int rc = shutdown(nat->rfcomm_sock, - shutdownInput ? SHUT_RD : SHUT_WR); - if (!rc) { - nat->rfcomm_connected &= - shutdownInput ? ~1 : ~2; - return JNI_TRUE; - } -#endif - return JNI_FALSE; -} - -static jint isConnectedNative(JNIEnv *env, jobject obj) { - LOGI(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - const native_data_t *nat = get_native_data(env, obj); - return nat->rfcomm_connected; -#endif - return 0; -} - -//@@@@@@@@@ bind to device??? -static jboolean bindNative(JNIEnv *env, jobject obj, jstring device, - jint port) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - - /* NOTE: If you change the code to modify nat, make sure you - add synchronize(this) to the method calling this native - method. - */ - const native_data_t *nat = get_native_data(env, obj); - if (nat->rfcomm_sock < 0) { - LOGE("socket(RFCOMM) error: socket not created"); - return JNI_FALSE; - } - - struct sockaddr_rc laddr; - int lm; - - lm = 0; -/* - lm |= RFCOMM_LM_MASTER; - lm |= RFCOMM_LM_AUTH; - lm |= RFCOMM_LM_ENCRYPT; - lm |= RFCOMM_LM_SECURE; -*/ - - if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { - LOGE("Can't set RFCOMM link mode"); - return JNI_FALSE; - } - - laddr.rc_family = AF_BLUETOOTH; - bacpy(&laddr.rc_bdaddr, BDADDR_ANY); - laddr.rc_channel = port; - - if (bind(nat->rfcomm_sock, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) { - LOGE("Can't bind RFCOMM socket"); - return JNI_FALSE; - } - - env->SetIntField(obj, field_mPort, port); - - return JNI_TRUE; -#endif - return JNI_FALSE; -} - -static jboolean listenNative(JNIEnv *env, jobject obj, jint backlog) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - /* NOTE: If you change the code to modify nat, make sure you - add synchronize(this) to the method calling this native - method. - */ - const native_data_t *nat = get_native_data(env, obj); - if (nat->rfcomm_sock < 0) { - LOGE("socket(RFCOMM) error: socket not created"); - return JNI_FALSE; - } - return listen(nat->rfcomm_sock, backlog) < 0 ? JNI_FALSE : JNI_TRUE; -#else - return JNI_FALSE; -#endif -} - -static int set_nb(int sk, bool nb) { - int flags = fcntl(sk, F_GETFL); - if (flags < 0) { - LOGE("Can't get socket flags with fcntl(): %s (%d)", - strerror(errno), errno); - close(sk); - return -1; - } - flags &= ~O_NONBLOCK; - if (nb) flags |= O_NONBLOCK; - int status = fcntl(sk, F_SETFL, flags); - if (status < 0) { - LOGE("Can't set socket to nonblocking mode with fcntl(): %s (%d)", - strerror(errno), errno); - close(sk); - return -1; - } - return 0; -} - -// Note: the code should check at a higher level to see whether -// listen() has been called. -#ifdef HAVE_BLUETOOTH -static int do_accept(JNIEnv* env, jobject object, int sock, - jobject newsock, - jfieldID out_address, - bool must_succeed) { - - if (must_succeed && set_nb(sock, true) < 0) - return -1; - - struct sockaddr_rc raddr; - int alen = sizeof(raddr); - int nsk = accept(sock, (struct sockaddr *) &raddr, &alen); - if (nsk < 0) { - LOGE("Error on accept from socket fd %d: %s (%d).", - sock, - strerror(errno), - errno); - if (must_succeed) set_nb(sock, false); - return -1; - } - - char addr[BTADDR_SIZE]; - get_bdaddr_as_string(&raddr.rc_bdaddr, addr); - env->SetObjectField(newsock, out_address, env->NewStringUTF(addr)); - - LOGI("Successful accept() on AG socket %d: new socket %d, address %s, RFCOMM channel %d", - sock, - nsk, - addr, - raddr.rc_channel); - if (must_succeed) set_nb(sock, false); - return nsk; -} -#endif /*HAVE_BLUETOOTH*/ - -static jobject acceptNative(JNIEnv *env, jobject obj, - jobject newsock, jint timeoutMs) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, obj); - if (nat->rfcomm_sock < 0) { - LOGE("socket(RFCOMM) error: socket not created"); - return JNI_FALSE; - } - - if (newsock == NULL) { - LOGE("%s: newsock = NULL\n", __FUNCTION__); - return JNI_FALSE; - } - - int nsk = -1; - if (timeoutMs < 0) { - /* block until accept() succeeds */ - nsk = do_accept(env, obj, nat->rfcomm_sock, - newsock, field_mAddress, false); - if (nsk < 0) { - return NULL; - } - } - else { - /* wait with a timeout */ - - struct pollfd fds; - fds.fd = nat->rfcomm_sock; - fds.events = POLLIN | POLLPRI | POLLOUT | POLLERR; - - env->SetIntField(obj, field_mAcceptTimeoutRemainingMs, 0); - int n = poll(&fds, 1, timeoutMs); - if (n <= 0) { - if (n < 0) { - LOGE("listening poll() on RFCOMM socket: %s (%d)", - strerror(errno), - errno); - env->SetIntField(obj, field_mAcceptTimeoutRemainingMs, timeoutMs); - } - else { - LOGI("listening poll() on RFCOMM socket timed out"); - } - return NULL; - } - - LOGI("listening poll() on RFCOMM socket returned %d", n); - if (fds.fd == nat->rfcomm_sock) { - if (fds.revents & (POLLIN | POLLPRI | POLLOUT)) { - LOGI("Accepting connection.\n"); - nsk = do_accept(env, obj, nat->rfcomm_sock, - newsock, field_mAddress, true); - if (nsk < 0) { - return NULL; - } - } - } - } - - LOGI("Connection accepted, new socket fd = %d.", nsk); - native_data_t *newnat = get_native_data(env, newsock); - newnat->rfcomm_sock = nsk; - newnat->rfcomm_connected = 3; - return jniCreateFileDescriptor(env, nsk); -#else - return NULL; -#endif -} - -static JNINativeMethod sMethods[] = { - /* name, signature, funcPtr */ - {"classInitNative", "()V", (void*)classInitNative}, - {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative}, - {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, - - {"createNative", "()Ljava/io/FileDescriptor;", (void *)createNative}, - {"destroyNative", "()V", (void *)destroyNative}, - {"connectNative", "(Ljava/lang/String;I)Z", (void *)connectNative}, - {"connectAsyncNative", "(Ljava/lang/String;I)Z", (void *)connectAsyncNative}, - {"interruptAsyncConnectNative", "()Z", (void *)interruptAsyncConnectNative}, - {"waitForAsyncConnectNative", "(I)I", (void *)waitForAsyncConnectNative}, - {"shutdownNative", "(Z)Z", (void *)shutdownNative}, - {"isConnectedNative", "()I", (void *)isConnectedNative}, - - {"bindNative", "(Ljava/lang/String;I)Z", (void*)bindNative}, - {"listenNative", "(I)Z", (void*)listenNative}, - {"acceptNative", "(Landroid/bluetooth/RfcommSocket;I)Ljava/io/FileDescriptor;", (void*)acceptNative}, -}; - -int register_android_bluetooth_RfcommSocket(JNIEnv *env) { - return AndroidRuntime::registerNativeMethods(env, - "android/bluetooth/RfcommSocket", sMethods, NELEM(sMethods)); -} - -} /* namespace android */ diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp index 0b8a604..343fa53 100644 --- a/core/jni/android_bluetooth_common.cpp +++ b/core/jni/android_bluetooth_common.cpp @@ -36,6 +36,43 @@ namespace android { #ifdef HAVE_BLUETOOTH + +static Properties remote_device_properties[] = { + {"Address", DBUS_TYPE_STRING}, + {"Name", DBUS_TYPE_STRING}, + {"Icon", DBUS_TYPE_STRING}, + {"Class", DBUS_TYPE_UINT32}, + {"UUIDs", DBUS_TYPE_ARRAY}, + {"Paired", DBUS_TYPE_BOOLEAN}, + {"Connected", DBUS_TYPE_BOOLEAN}, + {"Trusted", DBUS_TYPE_BOOLEAN}, + {"Alias", DBUS_TYPE_STRING}, + {"Nodes", DBUS_TYPE_ARRAY}, + {"Adapter", DBUS_TYPE_OBJECT_PATH}, + {"LegacyPairing", DBUS_TYPE_BOOLEAN}, + {"RSSI", DBUS_TYPE_INT16}, + {"TX", DBUS_TYPE_UINT32} +}; + +static Properties adapter_properties[] = { + {"Address", DBUS_TYPE_STRING}, + {"Name", DBUS_TYPE_STRING}, + {"Class", DBUS_TYPE_UINT32}, + {"Powered", DBUS_TYPE_BOOLEAN}, + {"Discoverable", DBUS_TYPE_BOOLEAN}, + {"DiscoverableTimeout", DBUS_TYPE_UINT32}, + {"Pairable", DBUS_TYPE_BOOLEAN}, + {"PairableTimeout", DBUS_TYPE_UINT32}, + {"Discovering", DBUS_TYPE_BOOLEAN}, + {"Devices", DBUS_TYPE_ARRAY}, +}; + +typedef union { + char *str_val; + int int_val; + char **array_val; +} property_value; + jfieldID get_field(JNIEnv *env, jclass clazz, const char *member, const char *mtype) { jfieldID field = env->GetFieldID(clazz, member, mtype); @@ -332,6 +369,44 @@ jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply) { return ret; } +static void set_object_array_element(JNIEnv *env, jobjectArray strArray, + const char *value, int index) { + jstring obj; + obj = env->NewStringUTF(value); + env->SetObjectArrayElement(strArray, index, obj); + env->DeleteLocalRef(obj); +} + +jobjectArray dbus_returns_array_of_object_path(JNIEnv *env, + DBusMessage *reply) { + + DBusError err; + char **list; + int i, len; + jobjectArray strArray = NULL; + + dbus_error_init(&err); + if (dbus_message_get_args (reply, + &err, + DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, + &list, &len, + DBUS_TYPE_INVALID)) { + jclass stringClass; + jstring classNameStr; + + stringClass = env->FindClass("java/lang/String"); + strArray = env->NewObjectArray(len, stringClass, NULL); + + for (i = 0; i < len; i++) + set_object_array_element(env, strArray, list[i], i); + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + } + + dbus_message_unref(reply); + return strArray; +} + jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) { DBusError err; @@ -353,11 +428,8 @@ jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) { stringClass = env->FindClass("java/lang/String"); strArray = env->NewObjectArray(len, stringClass, NULL); - for (i = 0; i < len; i++) { - //LOGV("%s: array[%d] = [%s]", __FUNCTION__, i, list[i]); - env->SetObjectArrayElement(strArray, i, - env->NewStringUTF(list[i])); - } + for (i = 0; i < len; i++) + set_object_array_element(env, strArray, list[i], i); } else { LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); } @@ -390,17 +462,263 @@ jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply) { return byteArray; } -void get_bdaddr(const char *str, bdaddr_t *ba) { +void append_variant(DBusMessageIter *iter, int type, void *val) +{ + DBusMessageIter value_iter; + char var_type[2] = { type, '\0'}; + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, var_type, &value_iter); + dbus_message_iter_append_basic(&value_iter, type, val); + dbus_message_iter_close_container(iter, &value_iter); +} + +int get_property(DBusMessageIter iter, Properties *properties, + int max_num_properties, int *prop_index, property_value *value, int *len) { + DBusMessageIter prop_val, array_val_iter; + char *property = NULL; + uint32_t array_type; + char *str_val; + int i, j, type, int_val; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return -1; + dbus_message_iter_get_basic(&iter, &property); + if (!dbus_message_iter_next(&iter)) + return -1; + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) + return -1; + for (i = 0; i < max_num_properties; i++) { + if (!strncmp(property, properties[i].name, strlen(property))) + break; + } + *prop_index = i; + if (i == max_num_properties) + return -1; + + dbus_message_iter_recurse(&iter, &prop_val); + type = properties[*prop_index].type; + if (dbus_message_iter_get_arg_type(&prop_val) != type) { + LOGE("Property type mismatch in get_property: %d, expected:%d, index:%d", + dbus_message_iter_get_arg_type(&prop_val), type, *prop_index); + return -1; + } + + switch(type) { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + dbus_message_iter_get_basic(&prop_val, &value->str_val); + *len = 1; + break; + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT16: + case DBUS_TYPE_BOOLEAN: + dbus_message_iter_get_basic(&prop_val, &int_val); + value->int_val = int_val; + *len = 1; + break; + case DBUS_TYPE_ARRAY: + dbus_message_iter_recurse(&prop_val, &array_val_iter); + array_type = dbus_message_iter_get_arg_type(&array_val_iter); + *len = 0; + value->array_val = NULL; + if (array_type == DBUS_TYPE_OBJECT_PATH || + array_type == DBUS_TYPE_STRING){ + j = 0; + do { + j ++; + } while(dbus_message_iter_next(&array_val_iter)); + dbus_message_iter_recurse(&prop_val, &array_val_iter); + // Allocate an array of char * + *len = j; + char **tmp = (char **)malloc(sizeof(char *) * *len); + if (!tmp) + return -1; + j = 0; + do { + dbus_message_iter_get_basic(&array_val_iter, &tmp[j]); + j ++; + } while(dbus_message_iter_next(&array_val_iter)); + value->array_val = tmp; + } + break; + default: + return -1; + } + return 0; +} + +void create_prop_array(JNIEnv *env, jobjectArray strArray, Properties *property, + property_value *value, int len, int *array_index ) { + char **prop_val = NULL; + char buf[32] = {'\0'}, buf1[32] = {'\0'}; + int i; + + char *name = property->name; + int prop_type = property->type; + + set_object_array_element(env, strArray, name, *array_index); + *array_index += 1; + + if (prop_type == DBUS_TYPE_UINT32 || prop_type == DBUS_TYPE_INT16) { + sprintf(buf, "%d", value->int_val); + set_object_array_element(env, strArray, buf, *array_index); + *array_index += 1; + } else if (prop_type == DBUS_TYPE_BOOLEAN) { + sprintf(buf, "%s", value->int_val ? "true" : "false"); + + set_object_array_element(env, strArray, buf, *array_index); + *array_index += 1; + } else if (prop_type == DBUS_TYPE_ARRAY) { + // Write the length first + sprintf(buf1, "%d", len); + set_object_array_element(env, strArray, buf1, *array_index); + *array_index += 1; + + prop_val = value->array_val; + for (i = 0; i < len; i++) { + set_object_array_element(env, strArray, prop_val[i], *array_index); + *array_index += 1; + } + } else { + set_object_array_element(env, strArray, (const char *) value->str_val, *array_index); + *array_index += 1; + } +} + +jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties, + const int max_num_properties) { + DBusMessageIter dict_entry, dict; + jobjectArray strArray = NULL; + property_value value; + int i, size = 0,array_index = 0; + int len = 0, prop_type = DBUS_TYPE_INVALID, prop_index = -1, type; + struct { + property_value value; + int len; + bool used; + } values[max_num_properties]; + int t, j; + + jclass stringClass = env->FindClass("java/lang/String"); + DBusError err; + dbus_error_init(&err); + + for (i = 0; i < max_num_properties; i++) { + values[i].used = false; + } + + if(dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + goto failure; + dbus_message_iter_recurse(iter, &dict); + do { + len = 0; + if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_DICT_ENTRY) + goto failure; + dbus_message_iter_recurse(&dict, &dict_entry); + + if (!get_property(dict_entry, properties, max_num_properties, &prop_index, + &value, &len)) { + size += 2; + if (properties[prop_index].type == DBUS_TYPE_ARRAY) + size += len; + values[prop_index].value = value; + values[prop_index].len = len; + values[prop_index].used = true; + } else { + goto failure; + } + } while(dbus_message_iter_next(&dict)); + + strArray = env->NewObjectArray(size, stringClass, NULL); + + for (i = 0; i < max_num_properties; i++) { + if (values[i].used) { + create_prop_array(env, strArray, &properties[i], &values[i].value, values[i].len, + &array_index); + + if (properties[i].type == DBUS_TYPE_ARRAY && values[i].used + && values[i].value.array_val != NULL) + free(values[i].value.array_val); + } + + } + return strArray; + +failure: + if (dbus_error_is_set(&err)) + LOG_AND_FREE_DBUS_ERROR(&err); + for (i = 0; i < max_num_properties; i++) + if (properties[i].type == DBUS_TYPE_ARRAY && values[i].used == true + && values[i].value.array_val != NULL) + free(values[i].value.array_val); + return NULL; +} + +jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg, + Properties *properties, int max_num_properties) { + DBusMessageIter iter; + DBusError err; + jobjectArray strArray = NULL; + jclass stringClass= env->FindClass("java/lang/String"); + int len = 0, prop_index = -1; + int array_index = 0, size = 0; + property_value value; + + dbus_error_init(&err); + if (!dbus_message_iter_init(msg, &iter)) + goto failure; + + if (!get_property(iter, properties, max_num_properties, + &prop_index, &value, &len)) { + size += 2; + if (properties[prop_index].type == DBUS_TYPE_ARRAY) + size += len; + strArray = env->NewObjectArray(size, stringClass, NULL); + + create_prop_array(env, strArray, &properties[prop_index], + &value, len, &array_index); + + if (properties[prop_index].type == DBUS_TYPE_ARRAY && value.array_val != NULL) + free(value.array_val); + + return strArray; + } +failure: + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return NULL; +} + +jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg) { + return parse_property_change(env, msg, (Properties *) &adapter_properties, + sizeof(adapter_properties) / sizeof(Properties)); +} + +jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg) { + return parse_property_change(env, msg, (Properties *) &remote_device_properties, + sizeof(remote_device_properties) / sizeof(Properties)); +} + +jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter) { + return parse_properties(env, iter, (Properties *) &adapter_properties, + sizeof(adapter_properties) / sizeof(Properties)); +} + +jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter) { + return parse_properties(env, iter, (Properties *) &remote_device_properties, + sizeof(remote_device_properties) / sizeof(Properties)); +} + +int get_bdaddr(const char *str, bdaddr_t *ba) { char *d = ((char *)ba) + 5, *endp; int i; for(i = 0; i < 6; i++) { *d-- = strtol(str, &endp, 16); if (*endp != ':' && i != 5) { memset(ba, 0, sizeof(bdaddr_t)); - return; + return -1; } str = endp + 1; } + return 0; } void get_bdaddr_as_string(const bdaddr_t *ba, char *str) { diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h index 69092dd..ef9b66b 100644 --- a/core/jni/android_bluetooth_common.h +++ b/core/jni/android_bluetooth_common.h @@ -68,6 +68,7 @@ jfieldID get_field(JNIEnv *env, struct event_loop_native_data_t { DBusConnection *conn; + const char *adapter; /* protects the thread */ pthread_mutex_t thread_mutex; @@ -89,6 +90,12 @@ struct event_loop_native_data_t { jobject me; }; +struct _Properties { + char name[32]; + int type; +}; +typedef struct _Properties Properties; + dbus_bool_t dbus_func_args_async(JNIEnv *env, DBusConnection *conn, int timeout_ms, @@ -142,9 +149,19 @@ jint dbus_returns_uint32(JNIEnv *env, DBusMessage *reply); jstring dbus_returns_string(JNIEnv *env, DBusMessage *reply); jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply); jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply); +jobjectArray dbus_returns_array_of_object_path(JNIEnv *env, DBusMessage *reply); jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply); -void get_bdaddr(const char *str, bdaddr_t *ba); +jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties, + const int max_num_properties); +jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg, + Properties *properties, int max_num_properties); +jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter); +jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter); +jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg); +jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg); +void append_variant(DBusMessageIter *iter, int type, void *val); +int get_bdaddr(const char *str, bdaddr_t *ba); void get_bdaddr_as_string(const bdaddr_t *ba, char *str); bool debug_no_encrypt(); diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index f19fbbf..91449bc 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -229,7 +229,7 @@ static jboolean isBlob_native(JNIEnv* env, jobject object, jint row, jint column { int32_t err; CursorWindow * window = GET_WINDOW(env, object); -LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window); +LOG_WINDOW("Checking if column is a blob or null for %d,%d from %p", row, column, window); field_slot_t field; err = window->read_field_slot(row, column, &field); @@ -241,6 +241,54 @@ LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL; } +static jboolean isString_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Checking if column is a string or null for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + return field.type == FIELD_TYPE_STRING || field.type == FIELD_TYPE_NULL; +} + +static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Checking if column is an integer for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + return field.type == FIELD_TYPE_INTEGER; +} + +static jboolean isFloat_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Checking if column is a float for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + return field.type == FIELD_TYPE_FLOAT; +} + static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column) { int32_t err; @@ -326,11 +374,11 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot"); return NULL; } - + jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField); if (buffer == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null"); - return NULL; + return NULL; } jchar* dst = env->GetCharArrayElements(buffer, NULL); uint8_t type = field.type; @@ -338,7 +386,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); jcharArray newArray = NULL; if (type == FIELD_TYPE_STRING) { uint32_t size = field.data.buffer.size; - if (size > 0) { + if (size > 0) { #if WINDOW_STORAGE_UTF8 // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); @@ -346,7 +394,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); if (strSize > bufferSize || dst == NULL) { newArray = env->NewCharArray(strSize); env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string()); - } else { + } else { memcpy(dst, (jchar const *)utf16.string(), strSize * 2); } sizeCopied = strSize; @@ -359,7 +407,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); } #endif - } + } } else if (type == FIELD_TYPE_INTEGER) { int64_t value; if (window->getLong(row, column, &value)) { @@ -628,6 +676,9 @@ static JNINativeMethod sMethods[] = {"putDouble_native", "(DII)Z", (void *)putDouble_native}, {"freeLastRow_native", "()V", (void *)freeLastRow}, {"putNull_native", "(II)Z", (void *)putNull_native}, + {"isString_native", "(II)Z", (void *)isString_native}, + {"isFloat_native", "(II)Z", (void *)isFloat_native}, + {"isInteger_native", "(II)Z", (void *)isInteger_native}, }; int register_android_database_CursorWindow(JNIEnv * env) @@ -646,7 +697,7 @@ int register_android_database_CursorWindow(JNIEnv * env) LOGE("Error locating fields"); return -1; } - + clazz = env->FindClass("android/database/CharArrayBuffer"); if (clazz == NULL) { LOGE("Can't find android/database/CharArrayBuffer"); diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp index 66858f9..020aff4 100644 --- a/core/jni/android_database_SQLiteDatabase.cpp +++ b/core/jni/android_database_SQLiteDatabase.cpp @@ -28,7 +28,10 @@ #include #include #include -#include +#include +#include +#include +#include #include #include diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp index 7d6e24f..63550fb 100644 --- a/core/jni/android_emoji_EmojiFactory.cpp +++ b/core/jni/android_emoji_EmojiFactory.cpp @@ -184,7 +184,7 @@ static jobject android_emoji_EmojiFactory_getBitmapFromAndroidPua( jobject obj = env->AllocObject(gBitmap_class); if (obj) { env->CallVoidMethod(obj, gBitmap_constructorMethodID, - reinterpret_cast(bitmap), false, NULL); + reinterpret_cast(bitmap), false, NULL, -1); if (env->ExceptionCheck() != 0) { LOGE("*** Uncaught exception returned from Java call!\n"); env->ExceptionDescribe(); @@ -197,8 +197,11 @@ static jobject android_emoji_EmojiFactory_getBitmapFromAndroidPua( static void android_emoji_EmojiFactory_destructor( JNIEnv* env, jobject obj, jint nativeEmojiFactory) { + /* + // Must not delete this object!! EmojiFactory *factory = reinterpret_cast(nativeEmojiFactory); delete factory; + */ } static jint android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificSjis( @@ -294,7 +297,7 @@ static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, int register_android_emoji_EmojiFactory(JNIEnv* env) { gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "", - "(IZ[B)V"); + "(IZ[BI)V"); gEmojiFactory_class = make_globalref(env, "android/emoji/EmojiFactory"); gEmojiFactory_constructorMethodID = env->GetMethodID( gEmojiFactory_class, "", "(ILjava/lang/String;)V"); diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 9053468..d57e526 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -23,9 +23,11 @@ #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" +#include + #include #include -#include +#include using namespace android; @@ -47,16 +49,23 @@ public: virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); virtual void postData(int32_t msgType, const sp& dataPtr); virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp& dataPtr); + void addCallbackBuffer(JNIEnv *env, jbyteArray cbb); + void setCallbackMode(JNIEnv *env, bool installed, bool manualMode); sp getCamera() { Mutex::Autolock _l(mLock); return mCamera; } void release(); private: void copyAndPost(JNIEnv* env, const sp& dataPtr, int msgType); + void clearCallbackBuffers_l(JNIEnv *env); jobject mCameraJObjectWeak; // weak reference to java object jclass mCameraJClass; // strong reference to java class - sp mCamera; // strong reference to native object + sp mCamera; // strong reference to native object Mutex mLock; + + Vector mCallbackBuffers; // Global reference application managed byte[] + bool mManualBufferMode; // Whether to use application managed buffers. + bool mManualCameraCallbackSet; // Whether the callback has been set, used to reduce unnecessary calls to set the callback. }; sp get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext) @@ -81,6 +90,9 @@ JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, mCameraJObjectWeak = env->NewGlobalRef(weak_this); mCameraJClass = (jclass)env->NewGlobalRef(clazz); mCamera = camera; + + mManualBufferMode = false; + mManualCameraCallbackSet = false; } void JNICameraContext::release() @@ -97,6 +109,7 @@ void JNICameraContext::release() env->DeleteGlobalRef(mCameraJClass); mCameraJClass = NULL; } + clearCallbackBuffers_l(env); mCamera.clear(); } @@ -112,7 +125,7 @@ void JNICameraContext::notify(int32_t msgType, int32_t ext1, int32_t ext2) } JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallStaticVoidMethod(mCameraJClass, fields.post_event, - mCameraJObjectWeak, msgType, ext1, ext2); + mCameraJObjectWeak, msgType, ext1, ext2, NULL); } void JNICameraContext::copyAndPost(JNIEnv* env, const sp& dataPtr, int msgType) @@ -129,7 +142,42 @@ void JNICameraContext::copyAndPost(JNIEnv* env, const sp& dataPtr, int if (heapBase != NULL) { const jbyte* data = reinterpret_cast(heapBase + offset); - obj = env->NewByteArray(size); + + if (!mManualBufferMode) { + LOGV("Allocating callback buffer"); + obj = env->NewByteArray(size); + } else { + // Vector access should be protected by lock in postData() + if(!mCallbackBuffers.isEmpty()) { + LOGV("Using callback buffer from queue of length %d", mCallbackBuffers.size()); + jbyteArray globalBuffer = mCallbackBuffers.itemAt(0); + mCallbackBuffers.removeAt(0); + + obj = (jbyteArray)env->NewLocalRef(globalBuffer); + env->DeleteGlobalRef(globalBuffer); + + if (obj != NULL) { + jsize bufferLength = env->GetArrayLength(obj); + if ((int)bufferLength < (int)size) { + LOGE("Manually set buffer was too small! Expected %d bytes, but got %d!", + size, bufferLength); + env->DeleteLocalRef(obj); + return; + } + } + } + + if(mCallbackBuffers.isEmpty()) { + LOGW("Out of buffers, clearing callback!"); + mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); + mManualCameraCallbackSet = false; + + if (obj == NULL) { + return; + } + } + } + if (obj == NULL) { LOGE("Couldn't allocate byte array for JPEG data"); env->ExceptionClear(); @@ -184,6 +232,62 @@ void JNICameraContext::postDataTimestamp(nsecs_t timestamp, int32_t msgType, con postData(msgType, dataPtr); } +void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualMode) +{ + Mutex::Autolock _l(mLock); + mManualBufferMode = manualMode; + mManualCameraCallbackSet = false; + + // In order to limit the over usage of binder threads, all non-manual buffer + // callbacks use FRAME_CALLBACK_FLAG_BARCODE_SCANNER mode now. + // + // Continuous callbacks will have the callback re-registered from handleMessage. + // Manual buffer mode will operate as fast as possible, relying on the finite supply + // of buffers for throttling. + + if (!installed) { + mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); + clearCallbackBuffers_l(env); + } else if (mManualBufferMode) { + if (!mCallbackBuffers.isEmpty()) { + mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA); + mManualCameraCallbackSet = true; + } + } else { + mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_BARCODE_SCANNER); + clearCallbackBuffers_l(env); + } +} + +void JNICameraContext::addCallbackBuffer(JNIEnv *env, jbyteArray cbb) +{ + if (cbb != NULL) { + Mutex::Autolock _l(mLock); + jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb); + mCallbackBuffers.push(cbb); + + LOGV("Adding callback buffer to queue, %d total", mCallbackBuffers.size()); + + // We want to make sure the camera knows we're ready for the next frame. + // This may have come unset had we not had a callbackbuffer ready for it last time. + if (mManualBufferMode && !mManualCameraCallbackSet) { + mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA); + mManualCameraCallbackSet = true; + } + } else { + LOGE("Null byte array!"); + } +} + +void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env) +{ + LOGV("Clearing callback buffers, %d remained", mCallbackBuffers.size()); + while(!mCallbackBuffers.isEmpty()) { + env->DeleteGlobalRef(mCallbackBuffers.top()); + mCallbackBuffers.pop(); + } +} + // connect to camera service static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) { @@ -297,8 +401,9 @@ static bool android_hardware_Camera_previewEnabled(JNIEnv *env, jobject thiz) return c->previewEnabled(); } -static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean oneshot) +static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean manualBuffer) { + LOGV("setHasPreviewCallback: installed:%d, manualBuffer:%d", (int)installed, (int)manualBuffer); // Important: Only install preview_callback if the Java code has called // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy // each preview frame for nothing. @@ -306,13 +411,19 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t sp camera = get_native_camera(env, thiz, &context); if (camera == 0) return; - int callback_flag; - if (installed) { - callback_flag = oneshot ? FRAME_CALLBACK_FLAG_BARCODE_SCANNER : FRAME_CALLBACK_FLAG_CAMERA; - } else { - callback_flag = FRAME_CALLBACK_FLAG_NOOP; + // setCallbackMode will take care of setting the context flags and calling + // camera->setPreviewCallbackFlags within a mutex for us. + context->setCallbackMode(env, installed, manualBuffer); +} + +static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes) { + LOGV("addCallbackBuffer"); + + JNICameraContext* context = reinterpret_cast(env->GetIntField(thiz, fields.context)); + + if (context != NULL) { + context->addCallbackBuffer(env, bytes); } - camera->setPreviewCallbackFlags(callback_flag); } static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz) @@ -327,6 +438,18 @@ static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz) } } +static void android_hardware_Camera_cancelAutoFocus(JNIEnv *env, jobject thiz) +{ + LOGV("cancelAutoFocus"); + JNICameraContext* context; + sp c = get_native_camera(env, thiz, &context); + if (c == 0) return; + + if (c->cancelAutoFocus() != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "cancelAutoFocus failed"); + } +} + static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz) { LOGV("takePicture"); @@ -379,20 +502,48 @@ static void android_hardware_Camera_reconnect(JNIEnv *env, jobject thiz) } } -static jint android_hardware_Camera_lock(JNIEnv *env, jobject thiz) +static void android_hardware_Camera_lock(JNIEnv *env, jobject thiz) { LOGV("lock"); sp camera = get_native_camera(env, thiz, NULL); - if (camera == 0) return INVALID_OPERATION; - return (jint) camera->lock(); + if (camera == 0) return; + + if (camera->lock() != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "lock failed"); + } } -static jint android_hardware_Camera_unlock(JNIEnv *env, jobject thiz) +static void android_hardware_Camera_unlock(JNIEnv *env, jobject thiz) { LOGV("unlock"); sp camera = get_native_camera(env, thiz, NULL); - if (camera == 0) return INVALID_OPERATION; - return (jint) camera->unlock(); + if (camera == 0) return; + + if (camera->unlock() != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "unlock failed"); + } +} + +static void android_hardware_Camera_startSmoothZoom(JNIEnv *env, jobject thiz, jint value) +{ + LOGD("startSmoothZoom"); + sp camera = get_native_camera(env, thiz, NULL); + if (camera == 0) return; + + if (camera->sendCommand(CAMERA_CMD_START_SMOOTH_ZOOM, value, 0) != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "start smooth zoom failed"); + } +} + +static void android_hardware_Camera_stopSmoothZoom(JNIEnv *env, jobject thiz) +{ + LOGD("stopSmoothZoom"); + sp camera = get_native_camera(env, thiz, NULL); + if (camera == 0) return; + + if (camera->sendCommand(CAMERA_CMD_STOP_SMOOTH_ZOOM, 0, 0) != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "stop smooth zoom failed"); + } } //------------------------------------------------- @@ -419,9 +570,15 @@ static JNINativeMethod camMethods[] = { { "setHasPreviewCallback", "(ZZ)V", (void *)android_hardware_Camera_setHasPreviewCallback }, + { "addCallbackBuffer", + "([B)V", + (void *)android_hardware_Camera_addCallbackBuffer }, { "native_autoFocus", "()V", (void *)android_hardware_Camera_autoFocus }, + { "native_cancelAutoFocus", + "()V", + (void *)android_hardware_Camera_cancelAutoFocus }, { "native_takePicture", "()V", (void *)android_hardware_Camera_takePicture }, @@ -435,11 +592,17 @@ static JNINativeMethod camMethods[] = { "()V", (void*)android_hardware_Camera_reconnect }, { "lock", - "()I", + "()V", (void*)android_hardware_Camera_lock }, { "unlock", - "()I", + "()V", (void*)android_hardware_Camera_unlock }, + { "startSmoothZoom", + "(I)V", + (void *)android_hardware_Camera_startSmoothZoom }, + { "stopSmoothZoom", + "()V", + (void *)android_hardware_Camera_stopSmoothZoom }, }; struct field { diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp index bf0bd65..f845878 100755 --- a/core/jni/android_location_GpsLocationProvider.cpp +++ b/core/jni/android_location_GpsLocationProvider.cpp @@ -16,33 +16,49 @@ #define LOG_TAG "GpsLocationProvider" +//#define LOG_NDDEBUG 0 + #include "JNIHelp.h" #include "jni.h" #include "hardware_legacy/gps.h" +#include "hardware_legacy/gps_ni.h" #include "utils/Log.h" #include "utils/misc.h" #include #include - static pthread_mutex_t sEventMutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t sEventCond = PTHREAD_COND_INITIALIZER; static jmethodID method_reportLocation; static jmethodID method_reportStatus; static jmethodID method_reportSvStatus; static jmethodID method_reportAGpsStatus; +static jmethodID method_reportNmea; static jmethodID method_xtraDownloadRequest; +static jmethodID method_reportNiNotification; static const GpsInterface* sGpsInterface = NULL; static const GpsXtraInterface* sGpsXtraInterface = NULL; static const AGpsInterface* sAGpsInterface = NULL; +static const GpsNiInterface* sGpsNiInterface = NULL; // data written to by GPS callbacks static GpsLocation sGpsLocation; static GpsStatus sGpsStatus; static GpsSvStatus sGpsSvStatus; static AGpsStatus sAGpsStatus; +static GpsNiNotification sGpsNiNotification; + +// buffer for NMEA data +#define NMEA_SENTENCE_LENGTH 100 +#define NMEA_SENTENCE_COUNT 40 +struct NmeaSentence { + GpsUtcTime timestamp; + char nmea[NMEA_SENTENCE_LENGTH]; +}; +static NmeaSentence sNmeaBuffer[NMEA_SENTENCE_LENGTH]; +static int mNmeaSentenceCount = 0; // a copy of the data shared by android_location_GpsLocationProvider_wait_for_event // and android_location_GpsLocationProvider_read_status @@ -50,6 +66,8 @@ static GpsLocation sGpsLocationCopy; static GpsStatus sGpsStatusCopy; static GpsSvStatus sGpsSvStatusCopy; static AGpsStatus sAGpsStatusCopy; +static NmeaSentence sNmeaBufferCopy[NMEA_SENTENCE_LENGTH]; +static GpsNiNotification sGpsNiNotificationCopy; enum CallbackType { kLocation = 1, @@ -58,6 +76,8 @@ enum CallbackType { kAGpsStatus = 8, kXtraDownloadRequest = 16, kDisableRequest = 32, + kNmeaAvailable = 64, + kNiNotification = 128, }; static int sPendingCallbacks; @@ -96,6 +116,30 @@ static void sv_status_callback(GpsSvStatus* sv_status) pthread_mutex_unlock(&sEventMutex); } +static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length) +{ + pthread_mutex_lock(&sEventMutex); + + if (length >= NMEA_SENTENCE_LENGTH) { + LOGE("NMEA data too long in nmea_callback (length = %d)\n", length); + length = NMEA_SENTENCE_LENGTH - 1; + } + if (mNmeaSentenceCount >= NMEA_SENTENCE_COUNT) { + LOGE("NMEA data overflowed buffer\n"); + pthread_mutex_unlock(&sEventMutex); + return; + } + + sPendingCallbacks |= kNmeaAvailable; + sNmeaBuffer[mNmeaSentenceCount].timestamp = timestamp; + memcpy(sNmeaBuffer[mNmeaSentenceCount].nmea, nmea, length); + sNmeaBuffer[mNmeaSentenceCount].nmea[length] = 0; + mNmeaSentenceCount++; + + pthread_cond_signal(&sEventCond); + pthread_mutex_unlock(&sEventMutex); +} + static void agps_status_callback(AGpsStatus* agps_status) { pthread_mutex_lock(&sEventMutex); @@ -111,6 +155,7 @@ GpsCallbacks sGpsCallbacks = { location_callback, status_callback, sv_status_callback, + nmea_callback }; static void @@ -122,6 +167,20 @@ download_request_callback() pthread_mutex_unlock(&sEventMutex); } +static void +gps_ni_notify_callback(GpsNiNotification *notification) +{ + LOGD("gps_ni_notify_callback: notif=%d", notification->notification_id); + + pthread_mutex_lock(&sEventMutex); + + sPendingCallbacks |= kNiNotification; + memcpy(&sGpsNiNotification, notification, sizeof(GpsNiNotification)); + + pthread_cond_signal(&sEventCond); + pthread_mutex_unlock(&sEventMutex); +} + GpsXtraCallbacks sGpsXtraCallbacks = { download_request_callback, }; @@ -130,12 +189,18 @@ AGpsCallbacks sAGpsCallbacks = { agps_status_callback, }; +GpsNiCallbacks sGpsNiCallbacks = { + gps_ni_notify_callback, +}; + static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V"); method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V"); method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II)V"); + method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(IJ)V"); method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V"); + method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V"); } static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) { @@ -155,6 +220,12 @@ static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject o sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); if (sAGpsInterface) sAGpsInterface->init(&sAGpsCallbacks); + + if (!sGpsNiInterface) + sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE); + if (sGpsNiInterface) + sGpsNiInterface->init(&sGpsNiCallbacks); + return true; } @@ -171,7 +242,7 @@ static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject ob sGpsInterface->cleanup(); } -static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj, jint positionMode, +static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj, jint positionMode, jboolean singleFix, jint fixFrequency) { int result = sGpsInterface->set_position_mode(positionMode, (singleFix ? 0 : fixFrequency)); @@ -195,46 +266,81 @@ static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* env, static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, jobject obj) { pthread_mutex_lock(&sEventMutex); - pthread_cond_wait(&sEventCond, &sEventMutex); - + while (sPendingCallbacks == 0) { + pthread_cond_wait(&sEventCond, &sEventMutex); + } + // copy and clear the callback flags int pendingCallbacks = sPendingCallbacks; sPendingCallbacks = 0; + int nmeaSentenceCount = mNmeaSentenceCount; + mNmeaSentenceCount = 0; // copy everything and unlock the mutex before calling into Java code to avoid the possibility // of timeouts in the GPS engine. - memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy)); - memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy)); - memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy)); - memcpy(&sAGpsStatusCopy, &sAGpsStatus, sizeof(sAGpsStatusCopy)); + if (pendingCallbacks & kLocation) + memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy)); + if (pendingCallbacks & kStatus) + memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy)); + if (pendingCallbacks & kSvStatus) + memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy)); + if (pendingCallbacks & kAGpsStatus) + memcpy(&sAGpsStatusCopy, &sAGpsStatus, sizeof(sAGpsStatusCopy)); + if (pendingCallbacks & kNmeaAvailable) + memcpy(&sNmeaBufferCopy, &sNmeaBuffer, nmeaSentenceCount * sizeof(sNmeaBuffer[0])); + if (pendingCallbacks & kNiNotification) + memcpy(&sGpsNiNotificationCopy, &sGpsNiNotification, sizeof(sGpsNiNotificationCopy)); pthread_mutex_unlock(&sEventMutex); - if (pendingCallbacks & kLocation) { + if (pendingCallbacks & kLocation) { env->CallVoidMethod(obj, method_reportLocation, sGpsLocationCopy.flags, (jdouble)sGpsLocationCopy.latitude, (jdouble)sGpsLocationCopy.longitude, - (jdouble)sGpsLocationCopy.altitude, - (jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing, + (jdouble)sGpsLocationCopy.altitude, + (jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing, (jfloat)sGpsLocationCopy.accuracy, (jlong)sGpsLocationCopy.timestamp); } if (pendingCallbacks & kStatus) { env->CallVoidMethod(obj, method_reportStatus, sGpsStatusCopy.status); - } + } if (pendingCallbacks & kSvStatus) { env->CallVoidMethod(obj, method_reportSvStatus); } if (pendingCallbacks & kAGpsStatus) { env->CallVoidMethod(obj, method_reportAGpsStatus, sAGpsStatusCopy.type, sAGpsStatusCopy.status); } - if (pendingCallbacks & kXtraDownloadRequest) { + if (pendingCallbacks & kNmeaAvailable) { + for (int i = 0; i < nmeaSentenceCount; i++) { + env->CallVoidMethod(obj, method_reportNmea, i, sNmeaBuffer[i].timestamp); + } + } + if (pendingCallbacks & kXtraDownloadRequest) { env->CallVoidMethod(obj, method_xtraDownloadRequest); } if (pendingCallbacks & kDisableRequest) { // don't need to do anything - we are just poking so wait_for_event will return. } + if (pendingCallbacks & kNiNotification) { + LOGD("android_location_GpsLocationProvider_wait_for_event: sent notification callback."); + jstring reqId = env->NewStringUTF(sGpsNiNotificationCopy.requestor_id); + jstring text = env->NewStringUTF(sGpsNiNotificationCopy.text); + jstring extras = env->NewStringUTF(sGpsNiNotificationCopy.extras); + env->CallVoidMethod(obj, method_reportNiNotification, + sGpsNiNotificationCopy.notification_id, + sGpsNiNotificationCopy.ni_type, + sGpsNiNotificationCopy.notify_flags, + sGpsNiNotificationCopy.timeout, + sGpsNiNotificationCopy.default_response, + reqId, + text, + sGpsNiNotificationCopy.requestor_id_encoding, + sGpsNiNotificationCopy.text_encoding, + extras + ); + } } -static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj, - jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray, +static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj, + jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray, jintArray maskArray) { // this should only be called from within a call to reportStatus, so we don't need to lock here @@ -264,6 +370,21 @@ static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, job return num_svs; } +static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj, jint index, jbyteArray nmeaArray, jint buffer_size) +{ + // this should only be called from within a call to reportNmea, so we don't need to lock here + + jbyte* nmea = env->GetByteArrayElements(nmeaArray, 0); + + int length = strlen(sNmeaBufferCopy[index].nmea); + if (length > buffer_size) + length = buffer_size; + memcpy(nmea, sNmeaBufferCopy[index].nmea, length); + + env->ReleaseByteArrayElements(nmeaArray, nmea, 0); + return length; +} + static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj, jlong time, jlong timeReference, jint uncertainty) { @@ -291,7 +412,7 @@ static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, return (sGpsXtraInterface != NULL); } -static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj, +static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj, jbyteArray data, jint length) { jbyte* bytes = env->GetByteArrayElements(data, 0); @@ -348,6 +469,16 @@ static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jo } } +static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj, + jint notifId, jint response) +{ + if (!sGpsNiInterface) + sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE); + if (sGpsNiInterface) { + sGpsNiInterface->respond(notifId, response); + } +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native}, @@ -360,6 +491,7 @@ static JNINativeMethod sMethods[] = { {"native_delete_aiding_data", "(I)V", (void*)android_location_GpsLocationProvider_delete_aiding_data}, {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event}, {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status}, + {"native_read_nmea", "(I[BI)I", (void*)android_location_GpsLocationProvider_read_nmea}, {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time}, {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location}, {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, @@ -368,6 +500,7 @@ static JNINativeMethod sMethods[] = { {"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed}, {"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed}, {"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server}, + {"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response}, }; int register_android_location_GpsLocationProvider(JNIEnv* env) diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index 44a9e8c..0be996d 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -28,8 +28,8 @@ #include "android_runtime/AndroidRuntime.h" #include "utils/Log.h" -#include "media/AudioSystem.h" #include "media/AudioRecord.h" +#include "media/mediarecorder.h" // ---------------------------------------------------------------------------- @@ -62,7 +62,7 @@ struct audiorecord_callback_cookie { #define AUDIORECORD_ERROR_BAD_VALUE -2 #define AUDIORECORD_ERROR_INVALID_OPERATION -3 #define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16 -#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT -17 +#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17 #define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18 #define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19 #define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20 @@ -122,17 +122,18 @@ static void recorderCallback(int event, void* user, void *info) { // ---------------------------------------------------------------------------- static int android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, - jint source, jint sampleRateInHertz, jint nbChannels, + jint source, jint sampleRateInHertz, jint channels, jint audioFormat, jint buffSizeInBytes) { //LOGV(">> Entering android_media_AudioRecord_setup"); - //LOGV("sampleRate=%d, audioFormat=%d, nbChannels=%d, buffSizeInBytes=%d", - // sampleRateInHertz, audioFormat, nbChannels, buffSizeInBytes); + //LOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d", + // sampleRateInHertz, audioFormat, channels, buffSizeInBytes); - if ((nbChannels == 0) || (nbChannels > 2)) { + if (!AudioSystem::isInputChannel(channels)) { LOGE("Error creating AudioRecord: channel count is not 1 or 2."); - return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT; + return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK; } + uint32_t nbChannels = AudioSystem::popCount(channels); // compare the format against the Java constants if ((audioFormat != javaAudioRecordFields.PCM16) @@ -152,12 +153,7 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, int frameSize = nbChannels * bytesPerSample; size_t frameCount = buffSizeInBytes / frameSize; - // convert and check input source value - // input_source values defined in AudioRecord.h are equal to - // JAVA MediaRecord.AudioSource values minus 1. - AudioRecord::input_source arSource = (AudioRecord::input_source)(source - 1); - if (arSource < AudioRecord::DEFAULT_INPUT || - arSource >= AudioRecord::NUM_INPUT_SOURCES) { + if (source >= AUDIO_SOURCE_LIST_END) { LOGE("Error creating AudioRecord: unknown source."); return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE; } @@ -184,10 +180,10 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, // we use a weak reference so the AudioRecord object can be garbage collected. lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); - lpRecorder->set(arSource, + lpRecorder->set(source, sampleRateInHertz, format, // word length, PCM - nbChannels, + channels, frameCount, 0, // flags recorderCallback,// callback_t diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 692610e..3d8d296 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -50,25 +50,6 @@ static int check_AudioSystem_Command(status_t status) } static int -android_media_AudioSystem_setVolume(JNIEnv *env, jobject clazz, jint type, jint volume) -{ - LOGV("setVolume(%d)", int(volume)); - - return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, AudioSystem::linearToLog(volume))); -} - -static int -android_media_AudioSystem_getVolume(JNIEnv *env, jobject clazz, jint type) -{ - float v; - int v_int = -1; - if (AudioSystem::getStreamVolume(int(type), &v) == NO_ERROR) { - v_int = AudioSystem::logToLinear(v); - } - return v_int; -} - -static int android_media_AudioSystem_muteMicrophone(JNIEnv *env, jobject thiz, jboolean on) { return check_AudioSystem_Command(AudioSystem::muteMicrophone(on)); @@ -82,34 +63,6 @@ android_media_AudioSystem_isMicrophoneMuted(JNIEnv *env, jobject thiz) return state; } -static int -android_media_AudioSystem_setRouting(JNIEnv *env, jobject clazz, jint mode, jint routes, jint mask) -{ - return check_AudioSystem_Command(AudioSystem::setRouting(mode, uint32_t(routes), uint32_t(mask))); -} - -static jint -android_media_AudioSystem_getRouting(JNIEnv *env, jobject clazz, jint mode) -{ - uint32_t routes = -1; - AudioSystem::getRouting(mode, &routes); - return jint(routes); -} - -static int -android_media_AudioSystem_setMode(JNIEnv *env, jobject clazz, jint mode) -{ - return check_AudioSystem_Command(AudioSystem::setMode(mode)); -} - -static jint -android_media_AudioSystem_getMode(JNIEnv *env, jobject clazz) -{ - int mode = AudioSystem::MODE_INVALID; - AudioSystem::getMode(&mode); - return jint(mode); -} - static jboolean android_media_AudioSystem_isMusicActive(JNIEnv *env, jobject thiz) { @@ -118,16 +71,29 @@ android_media_AudioSystem_isMusicActive(JNIEnv *env, jobject thiz) return state; } -// Temporary interface, do not use -// TODO: Replace with a more generic key:value get/set mechanism -static void -android_media_AudioSystem_setParameter(JNIEnv *env, jobject thiz, jstring key, jstring value) +static int +android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyValuePairs) { - const char *c_key = env->GetStringUTFChars(key, NULL); - const char *c_value = env->GetStringUTFChars(value, NULL); - AudioSystem::setParameter(c_key, c_value); - env->ReleaseStringUTFChars(key, c_key); - env->ReleaseStringUTFChars(value, c_value); + const jchar* c_keyValuePairs = env->GetStringCritical(keyValuePairs, 0); + String8 c_keyValuePairs8; + if (keyValuePairs) { + c_keyValuePairs8 = String8(c_keyValuePairs, env->GetStringLength(keyValuePairs)); + env->ReleaseStringCritical(keyValuePairs, c_keyValuePairs); + } + int status = check_AudioSystem_Command(AudioSystem::setParameters(0, c_keyValuePairs8)); + return status; +} + +static jstring +android_media_AudioSystem_getParameters(JNIEnv *env, jobject thiz, jstring keys) +{ + const jchar* c_keys = env->GetStringCritical(keys, 0); + String8 c_keys8; + if (keys) { + c_keys8 = String8(c_keys, env->GetStringLength(keys)); + env->ReleaseStringCritical(keys, c_keys); + } + return env->NewStringUTF(AudioSystem::getParameters(0, c_keys8).string()); } void android_media_AudioSystem_error_callback(status_t err) @@ -152,19 +118,93 @@ void android_media_AudioSystem_error_callback(status_t err) env->CallStaticVoidMethod(clazz, env->GetStaticMethodID(clazz, "errorCallbackFromNative","(I)V"), error); } +static int +android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address) +{ + const char *c_address = env->GetStringUTFChars(device_address, NULL); + int status = check_AudioSystem_Command(AudioSystem::setDeviceConnectionState(static_cast (device), + static_cast (state), + c_address)); + env->ReleaseStringUTFChars(device_address, c_address); + return status; +} + +static int +android_media_AudioSystem_getDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jstring device_address) +{ + const char *c_address = env->GetStringUTFChars(device_address, NULL); + int state = static_cast (AudioSystem::getDeviceConnectionState(static_cast (device), + c_address)); + env->ReleaseStringUTFChars(device_address, c_address); + return state; +} + +static int +android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state) +{ + return check_AudioSystem_Command(AudioSystem::setPhoneState(state)); +} + +static int +android_media_AudioSystem_setRingerMode(JNIEnv *env, jobject thiz, jint mode, jint mask) +{ + return check_AudioSystem_Command(AudioSystem::setRingerMode(mode, mask)); +} + +static int +android_media_AudioSystem_setForceUse(JNIEnv *env, jobject thiz, jint usage, jint config) +{ + return check_AudioSystem_Command(AudioSystem::setForceUse(static_cast (usage), + static_cast (config))); +} + +static int +android_media_AudioSystem_getForceUse(JNIEnv *env, jobject thiz, jint usage) +{ + return static_cast (AudioSystem::getForceUse(static_cast (usage))); +} + +static int +android_media_AudioSystem_initStreamVolume(JNIEnv *env, jobject thiz, jint stream, jint indexMin, jint indexMax) +{ + return check_AudioSystem_Command(AudioSystem::initStreamVolume(static_cast (stream), + indexMin, + indexMax)); +} + +static int +android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env, jobject thiz, jint stream, jint index) +{ + return check_AudioSystem_Command(AudioSystem::setStreamVolumeIndex(static_cast (stream), index)); +} + +static int +android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env, jobject thiz, jint stream) +{ + int index; + if (AudioSystem::getStreamVolumeIndex(static_cast (stream), &index) != NO_ERROR) { + index = -1; + } + return index; +} + // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { - {"setVolume", "(II)I", (void *)android_media_AudioSystem_setVolume}, - {"getVolume", "(I)I", (void *)android_media_AudioSystem_getVolume}, - {"setParameter", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)android_media_AudioSystem_setParameter}, + {"setParameters", "(Ljava/lang/String;)I", (void *)android_media_AudioSystem_setParameters}, + {"getParameters", "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_AudioSystem_getParameters}, {"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone}, {"isMicrophoneMuted", "()Z", (void *)android_media_AudioSystem_isMicrophoneMuted}, - {"setRouting", "(III)I", (void *)android_media_AudioSystem_setRouting}, - {"getRouting", "(I)I", (void *)android_media_AudioSystem_getRouting}, - {"setMode", "(I)I", (void *)android_media_AudioSystem_setMode}, - {"getMode", "()I", (void *)android_media_AudioSystem_getMode}, {"isMusicActive", "()Z", (void *)android_media_AudioSystem_isMusicActive}, + {"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState}, + {"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState}, + {"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState}, + {"setRingerMode", "(II)I", (void *)android_media_AudioSystem_setRingerMode}, + {"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse}, + {"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse}, + {"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume}, + {"setStreamVolumeIndex","(II)I", (void *)android_media_AudioSystem_setStreamVolumeIndex}, + {"getStreamVolumeIndex","(I)I", (void *)android_media_AudioSystem_getStreamVolumeIndex} }; const char* const kClassPathName = "android/media/AudioSystem"; diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index fd92fbe..65c0435 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -30,8 +30,8 @@ #include "media/AudioSystem.h" #include "media/AudioTrack.h" -#include -#include +#include +#include // ---------------------------------------------------------------------------- @@ -53,10 +53,11 @@ struct fields_t { int STREAM_MUSIC; //... stream type constants int STREAM_ALARM; //... stream type constants int STREAM_NOTIFICATION; //... stream type constants - int STREAM_BLUETOOTH_SCO; //... stream type constants + int STREAM_BLUETOOTH_SCO; //... stream type constants + int STREAM_DTMF; //... stream type constants int MODE_STREAM; //... memory mode int MODE_STATIC; //... memory mode - jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object + jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object jfieldID jniData; // stores in Java additional resources used by the native AudioTrack }; static fields_t javaAudioTrackFields; @@ -103,7 +104,7 @@ class AudioTrackJniStorage { #define AUDIOTRACK_ERROR_BAD_VALUE -2 #define AUDIOTRACK_ERROR_INVALID_OPERATION -3 #define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM -16 -#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT -17 +#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK -17 #define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT -18 #define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE -19 #define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED -20 @@ -164,11 +165,11 @@ static void audioCallback(int event, void* user, void *info) { // ---------------------------------------------------------------------------- static int android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, - jint streamType, jint sampleRateInHertz, jint nbChannels, + jint streamType, jint sampleRateInHertz, jint channels, jint audioFormat, jint buffSizeInBytes, jint memoryMode) { - LOGV("sampleRate=%d, audioFormat(from Java)=%d, nbChannels=%d, buffSize=%d", - sampleRateInHertz, audioFormat, nbChannels, buffSizeInBytes); + LOGV("sampleRate=%d, audioFormat(from Java)=%d, channels=%x, buffSize=%d", + sampleRateInHertz, audioFormat, channels, buffSizeInBytes); int afSampleRate; int afFrameCount; @@ -181,10 +182,11 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; } - if ((nbChannels == 0) || (nbChannels > 2)) { - LOGE("Error creating AudioTrack: channel count is not 1 or 2."); - return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT; + if (!AudioSystem::isOutputChannel(channels)) { + LOGE("Error creating AudioTrack: invalid channel mask."); + return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK; } + int nbChannels = AudioSystem::popCount(channels); // check the stream type AudioSystem::stream_type atStreamType; @@ -202,6 +204,8 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th atStreamType = AudioSystem::NOTIFICATION; } else if (streamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) { atStreamType = AudioSystem::BLUETOOTH_SCO; + } else if (streamType == javaAudioTrackFields.STREAM_DTMF) { + atStreamType = AudioSystem::DTMF; } else { LOGE("Error creating AudioTrack: unknown stream type."); return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE; @@ -231,15 +235,7 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1; int format = audioFormat == javaAudioTrackFields.PCM16 ? AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT; - int frameCount; - if (buffSizeInBytes == -1) { - // compute the frame count based on the system's output frame count - // and the native sample rate - frameCount = (sampleRateInHertz*afFrameCount)/afSampleRate; - } else { - // compute the frame count based on the specified buffer size - frameCount = buffSizeInBytes / (nbChannels * bytesPerSample); - } + int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample); AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage(); @@ -271,7 +267,7 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th atStreamType,// stream type sampleRateInHertz, format,// word length, PCM - nbChannels, + channels, frameCount, 0,// flags audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) @@ -291,7 +287,7 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th atStreamType,// stream type sampleRateInHertz, format,// word length, PCM - nbChannels, + channels, frameCount, 0,// flags audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); @@ -734,6 +730,8 @@ static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobjec nativeStreamType = AudioSystem::NOTIFICATION; } else if (javaStreamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) { nativeStreamType = AudioSystem::BLUETOOTH_SCO; + } else if (javaStreamType == javaAudioTrackFields.STREAM_DTMF) { + nativeStreamType = AudioSystem::DTMF; } else { nativeStreamType = AudioSystem::DEFAULT; } @@ -829,6 +827,7 @@ static JNINativeMethod gMethods[] = { #define JAVA_CONST_STREAM_ALARM_NAME "STREAM_ALARM" #define JAVA_CONST_STREAM_NOTIFICATION_NAME "STREAM_NOTIFICATION" #define JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME "STREAM_BLUETOOTH_SCO" +#define JAVA_CONST_STREAM_DTMF_NAME "STREAM_DTMF" #define JAVA_CONST_MODE_STREAM_NAME "MODE_STREAM" #define JAVA_CONST_MODE_STATIC_NAME "MODE_STATIC" #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" @@ -950,8 +949,10 @@ int register_android_media_AudioTrack(JNIEnv *env) JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION)) || !android_media_getIntConstantFromClass(env, audioManagerClass, JAVA_AUDIOMANAGER_CLASS_NAME, - JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME, - &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO))) { + JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME, &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO)) + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_CONST_STREAM_DTMF_NAME, &(javaAudioTrackFields.STREAM_DTMF))) { // error log performed in android_media_getIntConstantFromClass() return -1; } diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp index a4388de..50aa967 100644 --- a/core/jni/android_media_ToneGenerator.cpp +++ b/core/jni/android_media_ToneGenerator.cpp @@ -38,7 +38,7 @@ struct fields_t { }; static fields_t fields; -static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, jint toneType) { +static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, jint toneType, jint durationMs) { LOGV("android_media_ToneGenerator_startTone: %x\n", (int)thiz); ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz, @@ -48,7 +48,7 @@ static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, return false; } - return lpToneGen->startTone(toneType); + return lpToneGen->startTone(toneType, durationMs); } static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) { @@ -79,7 +79,7 @@ static void android_media_ToneGenerator_release(JNIEnv *env, jobject thiz) { static void android_media_ToneGenerator_native_setup(JNIEnv *env, jobject thiz, jint streamType, jint volume) { - ToneGenerator *lpToneGen = new ToneGenerator(streamType, AudioSystem::linearToLog(volume)); + ToneGenerator *lpToneGen = new ToneGenerator(streamType, AudioSystem::linearToLog(volume), true); env->SetIntField(thiz, fields.context, 0); @@ -120,7 +120,7 @@ static void android_media_ToneGenerator_native_finalize(JNIEnv *env, // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { - { "startTone", "(I)Z", (void *)android_media_ToneGenerator_startTone }, + { "startTone", "(II)Z", (void *)android_media_ToneGenerator_startTone }, { "stopTone", "()V", (void *)android_media_ToneGenerator_stopTone }, { "release", "()V", (void *)android_media_ToneGenerator_release }, { "native_setup", "(II)V", (void *)android_media_ToneGenerator_native_setup }, diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 8383deb..feb0dad 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -23,6 +23,7 @@ #include extern "C" { +int ifc_enable(const char *ifname); int ifc_disable(const char *ifname); int ifc_add_host_route(const char *ifname, uint32_t addr); int ifc_remove_host_routes(const char *ifname); @@ -66,6 +67,16 @@ static struct fieldIds { jfieldID leaseDuration; } dhcpInfoFieldIds; +static jint android_net_utils_enableInterface(JNIEnv* env, jobject clazz, jstring ifname) +{ + int result; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::ifc_enable(nameStr); + env->ReleaseStringUTFChars(ifname, nameStr); + return (jint)result; +} + static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstring ifname) { int result; @@ -209,6 +220,7 @@ static jboolean android_net_utils_configureInterface(JNIEnv* env, static JNINativeMethod gNetworkUtilMethods[] = { /* name, signature, funcPtr */ + { "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface }, { "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface }, { "addHostRoute", "(Ljava/lang/String;I)I", (void *)android_net_utils_addHostRoute }, { "removeHostRoutes", "(Ljava/lang/String;)I", (void *)android_net_utils_removeHostRoutes }, diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index ae744a8..38f3fda 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -311,18 +311,26 @@ static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject clazz) return result; } -static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz) +static jint android_net_wifi_getRssiHelper(const char *cmd) { char reply[256]; int rssi = -200; - if (doCommand("DRIVER RSSI", reply, sizeof(reply)) != 0) { + if (doCommand(cmd, reply, sizeof(reply)) != 0) { return (jint)-1; } + // reply comes back in the form " rssi XX" where XX is the // number we're interested in. if we're associating, it returns "OK". // beware - can contain spaces. if (strcmp(reply, "OK") != 0) { + // beware of trailing spaces + char* end = reply + strlen(reply); + while (end > reply && end[-1] == ' ') { + end--; + } + *end = 0; + char* lastSpace = strrchr(reply, ' '); // lastSpace should be preceded by "rssi" and followed by the value if (lastSpace && !strncmp(lastSpace - 4, "rssi", 4)) { @@ -332,6 +340,16 @@ static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz) return (jint)rssi; } +static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz) +{ + return android_net_wifi_getRssiHelper("DRIVER RSSI"); +} + +static jint android_net_wifi_getRssiApproxCommand(JNIEnv* env, jobject clazz) +{ + return android_net_wifi_getRssiHelper("DRIVER RSSI-APPROX"); +} + static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject clazz) { char reply[256]; @@ -527,6 +545,8 @@ static JNINativeMethod gWifiMethods[] = { { "setBluetoothCoexistenceScanModeCommand", "(Z)Z", (void*) android_net_wifi_setBluetoothCoexistenceScanModeCommand }, { "getRssiCommand", "()I", (void*) android_net_wifi_getRssiCommand }, + { "getRssiApproxCommand", "()I", + (void*) android_net_wifi_getRssiApproxCommand}, { "getLinkSpeedCommand", "()I", (void*) android_net_wifi_getLinkSpeedCommand }, { "getMacAddressCommand", "()Ljava/lang/String;", (void*) android_net_wifi_getMacAddressCommand }, { "saveConfigCommand", "()Z", (void*) android_net_wifi_saveConfigCommand }, diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp index ed8dfc8..44213ed 100644 --- a/core/jni/android_opengl_GLES11.cpp +++ b/core/jni/android_opengl_GLES11.cpp @@ -144,6 +144,10 @@ android_glBufferData__IILjava_nio_Buffer_2I if (data_buf) { data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining); + if (_remaining < size) { + _env->ThrowNew(IAEClass, "remaining() < size"); + goto exit; + } } glBufferData( (GLenum)target, @@ -151,6 +155,8 @@ android_glBufferData__IILjava_nio_Buffer_2I (GLvoid *)data, (GLenum)usage ); + +exit: if (_array) { releasePointer(_env, _array, data, JNI_FALSE); } @@ -165,12 +171,18 @@ android_glBufferSubData__IIILjava_nio_Buffer_2 GLvoid *data = (GLvoid *) 0; data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining); + if (_remaining < size) { + _env->ThrowNew(IAEClass, "remaining() < size"); + goto exit; + } glBufferSubData( (GLenum)target, (GLintptr)offset, (GLsizeiptr)size, (GLvoid *)data ); + +exit: if (_array) { releasePointer(_env, _array, data, JNI_FALSE); } diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index b4c60f1..3ee404a 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -200,12 +200,13 @@ static void load_maps(int pid, stats_t* stats) fclose(fp); } -static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object) +static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, + jint pid, jobject object) { stats_t stats; memset(&stats, 0, sizeof(stats_t)); - load_maps(getpid(), &stats); + load_maps(pid, &stats); env->SetIntField(object, dalvikPss_field, stats.dalvikPss); env->SetIntField(object, dalvikPrivateDirty_field, stats.dalvikPrivateDirty); @@ -220,6 +221,11 @@ static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject o env->SetIntField(object, otherSharedDirty_field, stats.otherSharedDirty); } +static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object) +{ + android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object); +} + static jint read_binder_stat(const char* stat) { FILE* fp = fopen(BINDER_STATS, "r"); @@ -281,6 +287,8 @@ static JNINativeMethod gMethods[] = { (void*) android_os_Debug_getNativeHeapFreeSize }, { "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V", (void*) android_os_Debug_getDirtyPages }, + { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V", + (void*) android_os_Debug_getDirtyPagesPid }, { "getBinderSentTransactions", "()I", (void*) android_os_Debug_getBinderSentTransactions }, { "getBinderReceivedTransactions", "()I", diff --git a/core/jni/android_os_Exec.cpp b/core/jni/android_os_Exec.cpp deleted file mode 100644 index ca5e695..0000000 --- a/core/jni/android_os_Exec.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Exec" - -#include "JNIHelp.h" -#include "jni.h" -#include "utils/Log.h" -#include "utils/misc.h" -#include "android_runtime/AndroidRuntime.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android -{ - -static jclass class_fileDescriptor; -static jfieldID field_fileDescriptor_descriptor; -static jmethodID method_fileDescriptor_init; - - -static int create_subprocess(const char *cmd, const char *arg0, const char *arg1, - int* pProcessId) -{ - char *devname; - int ptm; - pid_t pid; - - ptm = open("/dev/ptmx", O_RDWR); // | O_NOCTTY); - if(ptm < 0){ - LOGE("[ cannot open /dev/ptmx - %s ]\n",strerror(errno)); - return -1; - } - fcntl(ptm, F_SETFD, FD_CLOEXEC); - - if(grantpt(ptm) || unlockpt(ptm) || - ((devname = (char*) ptsname(ptm)) == 0)){ - LOGE("[ trouble with /dev/ptmx - %s ]\n", strerror(errno)); - return -1; - } - - pid = fork(); - if(pid < 0) { - LOGE("- fork failed: %s -\n", strerror(errno)); - return -1; - } - - if(pid == 0){ - int pts; - - setsid(); - - pts = open(devname, O_RDWR); - if(pts < 0) exit(-1); - - dup2(pts, 0); - dup2(pts, 1); - dup2(pts, 2); - - close(ptm); - - execl(cmd, cmd, arg0, arg1, NULL); - exit(-1); - } else { - *pProcessId = (int) pid; - return ptm; - } -} - - -static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, - jstring cmd, jstring arg0, jstring arg1, jintArray processIdArray) -{ - const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0; - String8 cmd_8; - if (str) { - cmd_8 = String8(str, env->GetStringLength(cmd)); - env->ReleaseStringCritical(cmd, str); - } - - str = arg0 ? env->GetStringCritical(arg0, 0) : 0; - const char* arg0Str = 0; - String8 arg0_8; - if (str) { - arg0_8 = String8(str, env->GetStringLength(arg0)); - env->ReleaseStringCritical(arg0, str); - arg0Str = arg0_8.string(); - } - - str = arg1 ? env->GetStringCritical(arg1, 0) : 0; - const char* arg1Str = 0; - String8 arg1_8; - if (str) { - arg1_8 = String8(str, env->GetStringLength(arg1)); - env->ReleaseStringCritical(arg1, str); - arg1Str = arg1_8.string(); - } - - int procId; - int ptm = create_subprocess(cmd_8.string(), arg0Str, arg1Str, &procId); - - if (processIdArray) { - int procIdLen = env->GetArrayLength(processIdArray); - if (procIdLen > 0) { - jboolean isCopy; - - int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy); - if (pProcId) { - *pProcId = procId; - env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0); - } - } - } - - jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init); - - if (!result) { - LOGE("Couldn't create a FileDescriptor."); - } - else { - env->SetIntField(result, field_fileDescriptor_descriptor, ptm); - } - - return result; -} - - -static void android_os_Exec_setPtyWindowSize(JNIEnv *env, jobject clazz, - jobject fileDescriptor, jint row, jint col, jint xpixel, jint ypixel) -{ - int fd; - struct winsize sz; - - fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - - if (env->ExceptionOccurred() != NULL) { - return; - } - - sz.ws_row = row; - sz.ws_col = col; - sz.ws_xpixel = xpixel; - sz.ws_ypixel = ypixel; - - ioctl(fd, TIOCSWINSZ, &sz); -} - -static int android_os_Exec_waitFor(JNIEnv *env, jobject clazz, - jint procId) { - int status; - waitpid(procId, &status, 0); - int result = 0; - if (WIFEXITED(status)) { - result = WEXITSTATUS(status); - } - return result; -} - -static JNINativeMethod method_table[] = { - { "createSubprocess", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)Ljava/io/FileDescriptor;", - (void*) android_os_Exec_createSubProcess }, - { "setPtyWindowSize", "(Ljava/io/FileDescriptor;IIII)V", - (void*) android_os_Exec_setPtyWindowSize}, - { "waitFor", "(I)I", - (void*) android_os_Exec_waitFor} -}; - -int register_android_os_Exec(JNIEnv *env) -{ - class_fileDescriptor = env->FindClass("java/io/FileDescriptor"); - - if (class_fileDescriptor == NULL) { - LOGE("Can't find java/io/FileDescriptor"); - return -1; - } - - field_fileDescriptor_descriptor = env->GetFieldID(class_fileDescriptor, "descriptor", "I"); - - if (field_fileDescriptor_descriptor == NULL) { - LOGE("Can't find FileDescriptor.descriptor"); - return -1; - } - - method_fileDescriptor_init = env->GetMethodID(class_fileDescriptor, "", "()V"); - if (method_fileDescriptor_init == NULL) { - LOGE("Can't find FileDescriptor.init"); - return -1; - } - - return AndroidRuntime::registerNativeMethods( - env, "android/os/Exec", - method_table, NELEM(method_table)); -} - -}; diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp index 8643393..1ae3ec7 100644 --- a/core/jni/android_os_MemoryFile.cpp +++ b/core/jni/android_os_MemoryFile.cpp @@ -118,7 +118,7 @@ static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDe } } -static jboolean android_os_MemoryFile_is_ashmem_region(JNIEnv* env, jobject clazz, +static jint android_os_MemoryFile_get_mapped_size(JNIEnv* env, jobject clazz, jobject fileDescriptor) { int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region. @@ -129,13 +129,13 @@ static jboolean android_os_MemoryFile_is_ashmem_region(JNIEnv* env, jobject claz if (errno == ENOTTY) { // ENOTTY means that the ioctl does not apply to this object, // i.e., it is not an ashmem region. - return JNI_FALSE; + return (jint) -1; } // Some other error, throw exception jniThrowIOException(env, errno); - return JNI_FALSE; + return (jint) -1; } - return JNI_TRUE; + return (jint) result; } static const JNINativeMethod methods[] = { @@ -146,8 +146,8 @@ static const JNINativeMethod methods[] = { {"native_read", "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read}, {"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write}, {"native_pin", "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin}, - {"native_is_ashmem_region", "(Ljava/io/FileDescriptor;)Z", - (void*)android_os_MemoryFile_is_ashmem_region} + {"native_get_mapped_size", "(Ljava/io/FileDescriptor;)I", + (void*)android_os_MemoryFile_get_mapped_size} }; static const char* const kClassPathName = "android/os/MemoryFile"; diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp index ca4fa11..406884b 100644 --- a/core/jni/android_os_SystemProperties.cpp +++ b/core/jni/android_os_SystemProperties.cpp @@ -36,9 +36,9 @@ static jstring SystemProperties_getSS(JNIEnv *env, jobject clazz, "key must not be null."); goto error; } - + key = env->GetStringUTFChars(keyJ, NULL); - + len = property_get(key, buf, ""); if ((len <= 0) && (defJ != NULL)) { rvJ = defJ; @@ -47,9 +47,9 @@ static jstring SystemProperties_getSS(JNIEnv *env, jobject clazz, } else { rvJ = env->NewStringUTF(""); } - + env->ReleaseStringUTFChars(keyJ, key); - + error: return rvJ; } @@ -60,6 +60,101 @@ static jstring SystemProperties_getS(JNIEnv *env, jobject clazz, return SystemProperties_getSS(env, clazz, keyJ, NULL); } +static jint SystemProperties_get_int(JNIEnv *env, jobject clazz, + jstring keyJ, jint defJ) +{ + int len; + const char* key; + char buf[PROPERTY_VALUE_MAX]; + jint result = defJ; + + if (keyJ == NULL) { + jniThrowException(env, "java/lang/NullPointerException", + "key must not be null."); + goto error; + } + + key = env->GetStringUTFChars(keyJ, NULL); + + len = property_get(key, buf, ""); + if (len > 0) { + jint temp; + if (sscanf(buf, "%d", &temp) == 1) + result = temp; + } + + env->ReleaseStringUTFChars(keyJ, key); + +error: + return result; +} + +static jlong SystemProperties_get_long(JNIEnv *env, jobject clazz, + jstring keyJ, jlong defJ) +{ + int len; + const char* key; + char buf[PROPERTY_VALUE_MAX]; + jlong result = defJ; + + if (keyJ == NULL) { + jniThrowException(env, "java/lang/NullPointerException", + "key must not be null."); + goto error; + } + + key = env->GetStringUTFChars(keyJ, NULL); + + len = property_get(key, buf, ""); + if (len > 0) { + jlong temp; + if (sscanf(buf, "%lld", &temp) == 1) + result = temp; + } + + env->ReleaseStringUTFChars(keyJ, key); + +error: + return result; +} + +static jboolean SystemProperties_get_boolean(JNIEnv *env, jobject clazz, + jstring keyJ, jboolean defJ) +{ + int len; + const char* key; + char buf[PROPERTY_VALUE_MAX]; + jboolean result = defJ; + + if (keyJ == NULL) { + jniThrowException(env, "java/lang/NullPointerException", + "key must not be null."); + goto error; + } + + key = env->GetStringUTFChars(keyJ, NULL); + + len = property_get(key, buf, ""); + if (len == 1) { + char ch = buf[0]; + if (ch == '0' || ch == 'n') + result = false; + else if (ch == '1' || ch == 'y') + result = true; + } else if (len > 1) { + if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) { + result = false; + } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) { + result = true; + } + } + + env->ReleaseStringUTFChars(keyJ, key); + +error: + return result; +} + static void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ, jstring valJ) { @@ -94,6 +189,12 @@ static JNINativeMethod method_table[] = { (void*) SystemProperties_getS }, { "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void*) SystemProperties_getSS }, + { "native_get_int", "(Ljava/lang/String;I)I", + (void*) SystemProperties_get_int }, + { "native_get_long", "(Ljava/lang/String;J)J", + (void*) SystemProperties_get_long }, + { "native_get_boolean", "(Ljava/lang/String;Z)Z", + (void*) SystemProperties_get_boolean }, { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) SystemProperties_set }, }; diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp index 91a8e8e..7a3bbbb 100644 --- a/core/jni/android_server_BluetoothA2dpService.cpp +++ b/core/jni/android_server_BluetoothA2dpService.cpp @@ -37,12 +37,7 @@ namespace android { #ifdef HAVE_BLUETOOTH -static jmethodID method_onHeadsetCreated; -static jmethodID method_onHeadsetRemoved; -static jmethodID method_onSinkConnected; -static jmethodID method_onSinkDisconnected; -static jmethodID method_onSinkPlaying; -static jmethodID method_onSinkStopped; +static jmethodID method_onSinkPropertyChanged; typedef struct { JavaVM *vm; @@ -53,11 +48,11 @@ typedef struct { static native_data_t *nat = NULL; // global native data -#endif - -#ifdef HAVE_BLUETOOTH -static void onConnectSinkResult(DBusMessage *msg, void *user, void *nat); -static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *nat); +static Properties sink_properties[] = { + {"State", DBUS_TYPE_STRING}, + {"Connected", DBUS_TYPE_BOOLEAN}, + {"Playing", DBUS_TYPE_BOOLEAN}, + }; #endif /* Returns true on success (even if adapter is present but disabled). @@ -100,213 +95,107 @@ static void cleanupNative(JNIEnv* env, jobject object) { } #endif } -static jobjectArray listHeadsetsNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, "/org/bluez/audio", - "org.bluez.audio.Manager", "ListHeadsets", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_array_of_strings(env, reply) : NULL; - } -#endif - return NULL; -} -static jstring createHeadsetNative(JNIEnv *env, jobject object, - jstring address) { +static jobjectArray getSinkPropertiesNative(JNIEnv *env, jobject object, + jstring path) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - LOGV("... address = %s\n", c_address); - DBusMessage *reply = - dbus_func_args(env, nat->conn, "/org/bluez/audio", - "org.bluez.audio.Manager", "CreateHeadset", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return reply ? dbus_returns_string(env, reply) : NULL; - } -#endif - return NULL; -} + DBusMessage *msg, *reply; + DBusError err; + dbus_error_init(&err); -static jstring removeHeadsetNative(JNIEnv *env, jobject object, jstring path) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, "/org/bluez/audio", - "org.bluez.audio.Manager", "RemoveHeadset", - DBUS_TYPE_STRING, &c_path, - DBUS_TYPE_INVALID); + reply = dbus_func_args_timeout(env, + nat->conn, -1, c_path, + "org.bluez.AudioSink", "GetProperties", + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); - return reply ? dbus_returns_string(env, reply) : NULL; + if (!reply && dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + return NULL; + } else if (!reply) { + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return NULL; + } + DBusMessageIter iter; + if (dbus_message_iter_init(reply, &iter)) + return parse_properties(env, &iter, (Properties *)&sink_properties, + sizeof(sink_properties) / sizeof(Properties)); } #endif return NULL; } -static jstring getAddressNative(JNIEnv *env, jobject object, jstring path) { + +static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, c_path, - "org.bluez.audio.Device", "GetAddress", - DBUS_TYPE_INVALID); + + bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat, + c_path, "org.bluez.AudioSink", "Connect", + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(path, c_path); - return reply ? dbus_returns_string(env, reply) : NULL; + return ret ? JNI_TRUE : JNI_FALSE; } #endif - return NULL; + return JNI_FALSE; } -static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) { +static jboolean disconnectSinkNative(JNIEnv *env, jobject object, + jstring path) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - size_t path_sz = env->GetStringUTFLength(path) + 1; - char *c_path_copy = (char *)malloc(path_sz); // callback data - strncpy(c_path_copy, c_path, path_sz); - - bool ret = - dbus_func_args_async(env, nat->conn, -1, - onConnectSinkResult, (void *)c_path_copy, nat, - c_path, - "org.bluez.audio.Sink", "Connect", - DBUS_TYPE_INVALID); + + bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat, + c_path, "org.bluez.AudioSink", "Disconnect", + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); - if (!ret) { - free(c_path_copy); - return JNI_FALSE; - } - return JNI_TRUE; + return ret ? JNI_TRUE : JNI_FALSE; } #endif return JNI_FALSE; } -static jboolean disconnectSinkNative(JNIEnv *env, jobject object, +static jboolean suspendSinkNative(JNIEnv *env, jobject object, jstring path) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - size_t path_sz = env->GetStringUTFLength(path) + 1; - char *c_path_copy = (char *)malloc(path_sz); // callback data - strncpy(c_path_copy, c_path, path_sz); - - bool ret = - dbus_func_args_async(env, nat->conn, -1, - onDisconnectSinkResult, (void *)c_path_copy, nat, - c_path, - "org.bluez.audio.Sink", "Disconnect", + bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat, + c_path, "org.bluez.audio.Sink", "Suspend", DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); - if (!ret) { - free(c_path_copy); - return JNI_FALSE; - } - return JNI_TRUE; + return ret ? JNI_TRUE : JNI_FALSE; } #endif return JNI_FALSE; } -static jboolean isSinkConnectedNative(JNIEnv *env, jobject object, jstring path) { +static jboolean resumeSinkNative(JNIEnv *env, jobject object, + jstring path) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, c_path, - "org.bluez.audio.Sink", "IsConnected", + bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat, + c_path, "org.bluez.audio.Sink", "Resume", DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); - return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; + return ret ? JNI_TRUE : JNI_FALSE; } #endif return JNI_FALSE; } #ifdef HAVE_BLUETOOTH -static void onConnectSinkResult(DBusMessage *msg, void *user, void *natData) { - LOGV(__FUNCTION__); - - char *c_path = (char *)user; - DBusError err; - JNIEnv *env; - - if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) { - LOGE("%s: error finding Env for our VM\n", __FUNCTION__); - return; - } - - dbus_error_init(&err); - - LOGV("... path = %s", c_path); - if (dbus_set_error_from_message(&err, msg)) { - /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */ - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - dbus_error_free(&err); - env->CallVoidMethod(nat->me, - method_onSinkDisconnected, - env->NewStringUTF(c_path)); - if (env->ExceptionCheck()) { - LOGE("VM Exception occurred in native function %s (%s:%d)", - __FUNCTION__, __FILE__, __LINE__); - } - } // else Java callback is triggered by signal in a2dp_event_filter - - free(c_path); -} - -static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *natData) { - LOGV(__FUNCTION__); - - char *c_path = (char *)user; - DBusError err; - JNIEnv *env; - - if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) { - LOGE("%s: error finding Env for our VM\n", __FUNCTION__); - return; - } - - dbus_error_init(&err); - - LOGV("... path = %s", c_path); - if (dbus_set_error_from_message(&err, msg)) { - /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */ - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - if (strcmp(err.name, "org.bluez.Error.NotConnected") == 0) { - // we were already disconnected, so report disconnect - env->CallVoidMethod(nat->me, - method_onSinkDisconnected, - env->NewStringUTF(c_path)); - } else { - // Assume it is still connected - env->CallVoidMethod(nat->me, - method_onSinkConnected, - env->NewStringUTF(c_path)); - } - dbus_error_free(&err); - if (env->ExceptionCheck()) { - LOGE("VM Exception occurred in native function %s (%s:%d)", - __FUNCTION__, __FILE__, __LINE__); - } - } // else Java callback is triggered by signal in a2dp_event_filter - - free(c_path); -} - DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) { DBusError err; @@ -324,71 +213,19 @@ DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) { DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - if (dbus_message_is_signal(msg, - "org.bluez.audio.Manager", - "HeadsetCreated")) { - char *c_path; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_path, - DBUS_TYPE_INVALID)) { - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onHeadsetCreated, - env->NewStringUTF(c_path)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Manager", - "HeadsetRemoved")) { - char *c_path; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_path, - DBUS_TYPE_INVALID)) { - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onHeadsetRemoved, - env->NewStringUTF(c_path)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Connected")) { + if (dbus_message_is_signal(msg, "org.bluez.AudioSink", + "PropertyChanged")) { + jobjectArray str_array = + parse_property_change(env, msg, (Properties *)&sink_properties, + sizeof(sink_properties) / sizeof(Properties)); const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); env->CallVoidMethod(nat->me, - method_onSinkConnected, - env->NewStringUTF(c_path)); + method_onSinkPropertyChanged, + env->NewStringUTF(c_path), + str_array); result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Disconnected")) { - const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onSinkDisconnected, - env->NewStringUTF(c_path)); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Playing")) { - const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onSinkPlaying, - env->NewStringUTF(c_path)); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Stopped")) { - const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onSinkStopped, - env->NewStringUTF(c_path)); - result = DBUS_HANDLER_RESULT_HANDLED; - } - - if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED) { + return result; + } else { LOGV("... ignored"); } if (env->ExceptionCheck()) { @@ -407,14 +244,13 @@ static JNINativeMethod sMethods[] = { {"initNative", "()Z", (void *)initNative}, {"cleanupNative", "()V", (void *)cleanupNative}, - /* Bluez audio 3.36 API */ - {"listHeadsetsNative", "()[Ljava/lang/String;", (void*)listHeadsetsNative}, - {"createHeadsetNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)createHeadsetNative}, - {"removeHeadsetNative", "(Ljava/lang/String;)Z", (void*)removeHeadsetNative}, - {"getAddressNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getAddressNative}, - {"connectSinkNative", "(Ljava/lang/String;)Z", (void*)connectSinkNative}, - {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void*)disconnectSinkNative}, - {"isSinkConnectedNative", "(Ljava/lang/String;)Z", (void*)isSinkConnectedNative}, + /* Bluez audio 4.40 API */ + {"connectSinkNative", "(Ljava/lang/String;)Z", (void *)connectSinkNative}, + {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void *)disconnectSinkNative}, + {"suspendSinkNative", "(Ljava/lang/String;)Z", (void*)suspendSinkNative}, + {"resumeSinkNative", "(Ljava/lang/String;)Z", (void*)resumeSinkNative}, + {"getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;", + (void *)getSinkPropertiesNative}, }; int register_android_server_BluetoothA2dpService(JNIEnv *env) { @@ -425,12 +261,8 @@ int register_android_server_BluetoothA2dpService(JNIEnv *env) { } #ifdef HAVE_BLUETOOTH - method_onHeadsetCreated = env->GetMethodID(clazz, "onHeadsetCreated", "(Ljava/lang/String;)V"); - method_onHeadsetRemoved = env->GetMethodID(clazz, "onHeadsetRemoved", "(Ljava/lang/String;)V"); - method_onSinkConnected = env->GetMethodID(clazz, "onSinkConnected", "(Ljava/lang/String;)V"); - method_onSinkDisconnected = env->GetMethodID(clazz, "onSinkDisconnected", "(Ljava/lang/String;)V"); - method_onSinkPlaying = env->GetMethodID(clazz, "onSinkPlaying", "(Ljava/lang/String;)V"); - method_onSinkStopped = env->GetMethodID(clazz, "onSinkStopped", "(Ljava/lang/String;)V"); + method_onSinkPropertyChanged = env->GetMethodID(clazz, "onSinkPropertyChanged", + "(Ljava/lang/String;[Ljava/lang/String;)V"); #endif return AndroidRuntime::registerNativeMethods(env, diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp deleted file mode 100644 index 58ae4f6..0000000 --- a/core/jni/android_server_BluetoothDeviceService.cpp +++ /dev/null @@ -1,1035 +0,0 @@ -/* -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Adapter" -#define LOG_TAG "BluetoothDeviceService.cpp" - -#include "android_bluetooth_common.h" -#include "android_runtime/AndroidRuntime.h" -#include "JNIHelp.h" -#include "jni.h" -#include "utils/Log.h" -#include "utils/misc.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifdef HAVE_BLUETOOTH -#include -#include -#endif - -#include - -namespace android { - -#define BLUETOOTH_CLASS_ERROR 0xFF000000 - -#ifdef HAVE_BLUETOOTH -// We initialize these variables when we load class -// android.server.BluetoothDeviceService -static jfieldID field_mNativeData; -static jfieldID field_mEventLoop; - -typedef struct { - JNIEnv *env; - DBusConnection *conn; - const char *adapter; // dbus object name of the local adapter -} native_data_t; - -extern event_loop_native_data_t *get_EventLoop_native_data(JNIEnv *, - jobject); -void onCreateBondingResult(DBusMessage *msg, void *user, void *nat); -void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *nat); - -/** Get native data stored in the opaque (Java code maintained) pointer mNativeData - * Perform quick sanity check, if there are any problems return NULL - */ -static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { - native_data_t *nat = - (native_data_t *)(env->GetIntField(object, field_mNativeData)); - if (nat == NULL || nat->conn == NULL) { - LOGE("Uninitialized native data\n"); - return NULL; - } - return nat; -} -#endif - -static void classInitNative(JNIEnv* env, jclass clazz) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - field_mNativeData = get_field(env, clazz, "mNativeData", "I"); - field_mEventLoop = get_field(env, clazz, "mEventLoop", - "Landroid/server/BluetoothEventLoop;"); -#endif -} - -/* Returns true on success (even if adapter is present but disabled). - * Return false if dbus is down, or another serious error (out of memory) -*/ -static bool initializeNativeDataNative(JNIEnv* env, jobject object) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t)); - if (NULL == nat) { - LOGE("%s: out of memory!", __FUNCTION__); - return false; - } - nat->env = env; - - env->SetIntField(object, field_mNativeData, (jint)nat); - DBusError err; - dbus_error_init(&err); - dbus_threads_init_default(); - nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); - if (dbus_error_is_set(&err)) { - LOGE("Could not get onto the system bus: %s", err.message); - dbus_error_free(&err); - return false; - } - dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); - - nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME; -#endif /*HAVE_BLUETOOTH*/ - return true; -} - -static void cleanupNativeDataNative(JNIEnv* env, jobject object) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = - (native_data_t *)env->GetIntField(object, field_mNativeData); - if (nat) { - free(nat); - nat = NULL; - } -#endif -} - -static jstring getNameNative(JNIEnv *env, jobject object){ - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetName", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_string(env, reply) : NULL; - } -#endif - return NULL; -} - -static jstring getAdapterPathNative(JNIEnv *env, jobject object) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); - if (nat) { - return (env->NewStringUTF(nat->adapter)); - } -#endif - return NULL; -} - - -static jboolean startDiscoveryNative(JNIEnv *env, jobject object) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - DBusMessage *msg = NULL; - DBusMessage *reply = NULL; - DBusError err; - const char *name; - jboolean ret = JNI_FALSE; - - native_data_t *nat = get_native_data(env, object); - if (nat == NULL) { - goto done; - } - - dbus_error_init(&err); - - /* Compose the command */ - msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, - DBUS_CLASS_NAME, "DiscoverDevices"); - - if (msg == NULL) { - LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__); - goto done; - } - - /* Send the command. */ - reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); - if (dbus_error_is_set(&err)) { - /* We treat the in-progress error code as success. */ - if(strcmp(err.message, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { - LOGW("%s: D-Bus error: %s, treating as startDiscoveryNative success\n", - __FUNCTION__, err.message); - ret = JNI_TRUE; - goto done; - } else { - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - ret = JNI_FALSE; - goto done; - } - } - - ret = JNI_TRUE; -done: - if (reply) dbus_message_unref(reply); - if (msg) dbus_message_unref(msg); - return ret; -#else - return JNI_FALSE; -#endif -} - -static void cancelDiscoveryNative(JNIEnv *env, jobject object) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - DBusMessage *msg = NULL; - DBusMessage *reply = NULL; - DBusError err; - const char *name; - jstring ret; - native_data_t *nat; - - dbus_error_init(&err); - - nat = get_native_data(env, object); - if (nat == NULL) { - goto done; - } - - /* Compose the command */ - msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, - DBUS_CLASS_NAME, "CancelDiscovery"); - - if (msg == NULL) { - LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__); - goto done; - } - - /* Send the command. */ - reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); - if (dbus_error_is_set(&err)) { - if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized") == 0) { - // hcid sends this if there is no active discovery to cancel - LOGV("%s: There was no active discovery to cancel", __FUNCTION__); - dbus_error_free(&err); - } else { - LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - } - } - -done: - if (msg) dbus_message_unref(msg); - if (reply) dbus_message_unref(reply); -#endif -} - -static jboolean startPeriodicDiscoveryNative(JNIEnv *env, jobject object) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - DBusMessage *msg = NULL; - DBusMessage *reply = NULL; - DBusError err; - jboolean ret = JNI_FALSE; - - native_data_t *nat = get_native_data(env, object); - if (nat == NULL) { - goto done; - } - - dbus_error_init(&err); - msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, - DBUS_CLASS_NAME, "StartPeriodicDiscovery"); - if (msg == NULL) { - LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__); - goto done; - } - - /* Send the command. */ - reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); - if (dbus_error_is_set(&err)) { - /* We treat the in-progress error code as success. */ - if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { - LOGW("%s: D-Bus error: %s (%s), treating as " - "startPeriodicDiscoveryNative success\n", - __FUNCTION__, err.name, err.message); - } else { - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - ret = JNI_FALSE; - goto done; - } - } - - ret = JNI_TRUE; -done: - if (reply) dbus_message_unref(reply); - if (msg) dbus_message_unref(msg); - return ret; -#else - return JNI_FALSE; -#endif -} - -static jboolean stopPeriodicDiscoveryNative(JNIEnv *env, jobject object) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - DBusMessage *msg = NULL; - DBusMessage *reply = NULL; - DBusError err; - const char *name; - jboolean ret = JNI_FALSE; - - native_data_t *nat = get_native_data(env, object); - if (nat == NULL) { - goto done; - } - - dbus_error_init(&err); - msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, - DBUS_CLASS_NAME, "StopPeriodicDiscovery"); - if (msg == NULL) { - LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__); - goto done; - } - - /* Send the command. */ - reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); - if (dbus_error_is_set(&err)) { - /* We treat the in-progress error code as success. */ - if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { - LOGW("%s: D-Bus error: %s (%s), treating as " - "stopPeriodicDiscoveryNative success\n", - __FUNCTION__, err.name, err.message); - } else { - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - ret = JNI_FALSE; - goto done; - } - } - - ret = JNI_TRUE; -done: - if (reply) dbus_message_unref(reply); - if (msg) dbus_message_unref(msg); - return ret; -#else - return JNI_FALSE; -#endif -} - -static jboolean isPeriodicDiscoveryNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "IsPeriodicDiscovery", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -static jboolean setDiscoverableTimeoutNative(JNIEnv *env, jobject object, jint timeout_s) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - - if (timeout_s < 0) { - return JNI_FALSE; - } - - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "SetDiscoverableTimeout", - DBUS_TYPE_UINT32, &timeout_s, - DBUS_TYPE_INVALID); - if (reply != NULL) { - dbus_message_unref(reply); - return JNI_TRUE; - } - } -#endif - return JNI_FALSE; -} - -static jint getDiscoverableTimeoutNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetDiscoverableTimeout", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_uint32(env, reply) : -1; - } -#endif - return -1; -} - -static jboolean isConnectedNative(JNIEnv *env, jobject object, jstring address) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "IsConnected", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -static void disconnectRemoteDeviceNative(JNIEnv *env, jobject object, jstring address) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - // Set a timeout of 5 seconds. Specifying the default timeout is - // not long enough, as a remote-device disconnect results in - // signal RemoteDisconnectRequested being sent, followed by a - // delay of 2 seconds, after which the actual disconnect takes - // place. - DBusMessage *reply = - dbus_func_args_timeout(env, nat->conn, 60000, nat->adapter, - DBUS_CLASS_NAME, "DisconnectRemoteDevice", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - if (reply) dbus_message_unref(reply); - } -#endif -} - -static jstring getModeNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetMode", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_string(env, reply) : NULL; - } -#endif - return NULL; -} - -static jboolean setModeNative(JNIEnv *env, jobject object, jstring mode) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_mode = env->GetStringUTFChars(mode, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "SetMode", - DBUS_TYPE_STRING, &c_mode, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(mode, c_mode); - if (reply) { - dbus_message_unref(reply); - return JNI_TRUE; - } - return JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -static jboolean createBondingNative(JNIEnv *env, jobject object, - jstring address, jint timeout_ms) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); - jobject eventLoop = env->GetObjectField(object, field_mEventLoop); - struct event_loop_native_data_t *eventLoopNat = - get_EventLoop_native_data(env, eventLoop); - - if (nat && eventLoopNat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - LOGV("... address = %s", c_address); - char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); - strlcpy(context_address, c_address, BTADDR_SIZE); // for callback - bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms, - onCreateBondingResult, // callback - context_address, - eventLoopNat, - nat->adapter, - DBUS_CLASS_NAME, "CreateBonding", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return ret ? JNI_TRUE : JNI_FALSE; - - } -#endif - return JNI_FALSE; -} - -static jboolean cancelBondingProcessNative(JNIEnv *env, jobject object, - jstring address) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - LOGV("... address = %s", c_address); - DBusMessage *reply = - dbus_func_args_timeout(env, nat->conn, -1, nat->adapter, - DBUS_CLASS_NAME, "CancelBondingProcess", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - if (reply) { - dbus_message_unref(reply); - } - return JNI_TRUE; - } -#endif - return JNI_FALSE; -} - -static jboolean removeBondingNative(JNIEnv *env, jobject object, jstring address) { - LOGV(__FUNCTION__); - jboolean result = JNI_FALSE; -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - LOGV("... address = %s", c_address); - DBusError err; - dbus_error_init(&err); - DBusMessage *reply = - dbus_func_args_error(env, nat->conn, &err, nat->adapter, - DBUS_CLASS_NAME, "RemoveBonding", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - if (dbus_error_is_set(&err)) { - if (dbus_error_has_name(&err, - BLUEZ_DBUS_BASE_IFC ".Error.DoesNotExist")) { - LOGW("%s: Warning: %s (%s)", __FUNCTION__, err.message, - c_address); - result = JNI_TRUE; - } else { - LOGE("%s: D-Bus error %s (%s)", __FUNCTION__, err.name, - err.message); - } - } else { - result = JNI_TRUE; - } - - env->ReleaseStringUTFChars(address, c_address); - dbus_error_free(&err); - if (reply) dbus_message_unref(reply); - } -#endif - return result; -} - -static jobjectArray listBondingsNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "ListBondings", - DBUS_TYPE_INVALID); - // return String[] - return reply ? dbus_returns_array_of_strings(env, reply) : NULL; - } -#endif - return NULL; -} - -static jobjectArray listConnectionsNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "ListConnections", - DBUS_TYPE_INVALID); - // return String[] - return reply ? dbus_returns_array_of_strings(env, reply) : NULL; - } -#endif - return NULL; -} - -static jobjectArray listRemoteDevicesNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "ListRemoteDevices", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_array_of_strings(env, reply) : NULL; - } -#endif - return NULL; -} - -static jstring common_Get(JNIEnv *env, jobject object, const char *func) { - LOGV("%s:%s", __FUNCTION__, func); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusError err; - dbus_error_init(&err); - DBusMessage *reply = - dbus_func_args_error(env, nat->conn, &err, nat->adapter, - DBUS_CLASS_NAME, func, - DBUS_TYPE_INVALID); - if (reply) { - return dbus_returns_string(env, reply); - } else { - LOG_AND_FREE_DBUS_ERROR(&err); - return NULL; - } - } -#endif - return NULL; -} - -static jstring getAddressNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetAddress"); -} - -static jstring getVersionNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetVersion"); -} - -static jstring getRevisionNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetRevision"); -} - -static jstring getManufacturerNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetManufacturer"); -} - -static jstring getCompanyNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetCompany"); -} - -static jboolean setNameNative(JNIEnv *env, jobject obj, jstring name) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, obj); - if (nat) { - const char *c_name = env->GetStringUTFChars(name, NULL); - DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "SetName", - DBUS_TYPE_STRING, &c_name, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(name, c_name); - if (reply) { - dbus_message_unref(reply); - return JNI_TRUE; - } - } -#endif - return JNI_FALSE; -} - -static jstring common_getRemote(JNIEnv *env, jobject object, const char *func, - jstring address) { - LOGV("%s:%s", __FUNCTION__, func); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - DBusError err; - dbus_error_init(&err); - - LOGV("... address = %s", c_address); - - DBusMessage *reply = - dbus_func_args_error(env, nat->conn, &err, nat->adapter, - DBUS_CLASS_NAME, func, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - if (reply) { - return dbus_returns_string(env, reply); - } else if (!strcmp(func, "GetRemoteName") && - dbus_error_has_name(&err, "org.bluez.Error.RequestDeferred")) { - // This error occurs if we request name during device discovery, - // its fine - LOGV("... %s: %s", func, err.message); - dbus_error_free(&err); - return NULL; - } else { - LOG_AND_FREE_DBUS_ERROR(&err); - return NULL; - } - } -#endif - return NULL; -} - -static jstring getRemoteVersionNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteVersion", address); -} - -static jstring getRemoteRevisionNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteRevision", address); -} - -static jstring getRemoteManufacturerNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteManufacturer", address); -} - -static jstring getRemoteCompanyNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteCompany", address); -} - -static jstring getRemoteNameNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteName", address); -} - -static jstring lastSeenNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "LastSeen", address); -} - -static jstring lastUsedNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "LastUsed", address); -} - -static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) { - jint result = BLUETOOTH_CLASS_ERROR; -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - - LOGV("... address = %s", c_address); - - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetRemoteClass", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - if (reply) - { - DBusError err; - dbus_error_init(&err); - if (!dbus_message_get_args(reply, &err, - DBUS_TYPE_UINT32, &result, - DBUS_TYPE_INVALID)) { - LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); - } - dbus_message_unref(reply); - } - } -#endif - return result; -} - -static jbyteArray getRemoteFeaturesNative(JNIEnv *env, jobject object, - jstring address) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - - LOGV("... address = %s", c_address); - - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetRemoteFeatures", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - /* array of DBUS_TYPE_BYTE_AS_STRING */ - return reply ? dbus_returns_array_of_bytes(env, reply) : NULL; - } -#endif - return NULL; -} - -static jintArray getRemoteServiceHandlesNative(JNIEnv *env, jobject object, - jstring address, jstring match) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - jintArray intArray = NULL; - const char *c_address = env->GetStringUTFChars(address, NULL); - const char *c_match = env->GetStringUTFChars(match, NULL); - - LOGV("... address = %s match = %s", c_address, c_match); - - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetRemoteServiceHandles", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_STRING, &c_match, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - env->ReleaseStringUTFChars(match, c_match); - if (reply) - { - DBusError err; - jint *list; - int i, len; - - dbus_error_init(&err); - if (dbus_message_get_args (reply, &err, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, - &list, &len, - DBUS_TYPE_INVALID)) { - if (len) { - intArray = env->NewIntArray(len); - if (intArray) - env->SetIntArrayRegion(intArray, 0, len, list); - } - } else { - LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); - } - - dbus_message_unref(reply); - } - return intArray; - } -#endif - return NULL; -} - -static jbyteArray getRemoteServiceRecordNative(JNIEnv *env, jobject object, - jstring address, jint handle) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - - LOGV("... address = %s", c_address); - - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetRemoteServiceRecord", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return reply ? dbus_returns_array_of_bytes(env, reply) : NULL; - } -#endif - return NULL; -} - -static jboolean getRemoteServiceChannelNative(JNIEnv *env, jobject object, - jstring address, jshort uuid16) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - jobject eventLoop = env->GetObjectField(object, field_mEventLoop); - struct event_loop_native_data_t *eventLoopNat = - get_EventLoop_native_data(env, eventLoop); - if (nat && eventLoopNat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); - strlcpy(context_address, c_address, BTADDR_SIZE); - - LOGV("... address = %s", c_address); - LOGV("... uuid16 = %#X", uuid16); - - bool ret = dbus_func_args_async(env, nat->conn, 20000, // ms - onGetRemoteServiceChannelResult, context_address, - eventLoopNat, - nat->adapter, - DBUS_CLASS_NAME, "GetRemoteServiceChannel", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_UINT16, &uuid16, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return ret ? JNI_TRUE : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -static jint enableNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - return bt_enable(); -#endif - return -1; -} - -static jint disableNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - return bt_disable(); -#endif - return -1; -} - -static jint isEnabledNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - return bt_is_enabled(); -#endif - return -1; -} - -static jboolean setPinNative(JNIEnv *env, jobject object, jstring address, - jstring pin, int nativeData) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *msg = (DBusMessage *)nativeData; - DBusMessage *reply = dbus_message_new_method_return(msg); - if (!reply) { - LOGE("%s: Cannot create message reply to return PIN code to " - "D-Bus\n", __FUNCTION__); - dbus_message_unref(msg); - return JNI_FALSE; - } - - const char *c_pin = env->GetStringUTFChars(pin, NULL); - - dbus_message_append_args(reply, DBUS_TYPE_STRING, &c_pin, - DBUS_TYPE_INVALID); - - dbus_connection_send(nat->conn, reply, NULL); - dbus_message_unref(msg); - dbus_message_unref(reply); - env->ReleaseStringUTFChars(pin, c_pin); - return JNI_TRUE; - } -#endif - return JNI_FALSE; -} - -static jboolean cancelPinNative(JNIEnv *env, jobject object, jstring address, - int nativeData) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *msg = (DBusMessage *)nativeData; - DBusMessage *reply = dbus_message_new_error(msg, - "org.bluez.Error.Canceled", "PIN Entry was canceled"); - if (!reply) { - LOGE("%s: Cannot create message reply to return PIN cancel to " - "D-BUS\n", __FUNCTION__); - dbus_message_unref(msg); - return JNI_FALSE; - } - - dbus_connection_send(nat->conn, reply, NULL); - dbus_message_unref(msg); - dbus_message_unref(reply); - return JNI_TRUE; - } -#endif - return JNI_FALSE; -} - -static JNINativeMethod sMethods[] = { - /* name, signature, funcPtr */ - {"classInitNative", "()V", (void*)classInitNative}, - {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative}, - {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, - {"getAdapterPathNative", "()Ljava/lang/String;", (void*)getAdapterPathNative}, - - {"isEnabledNative", "()I", (void *)isEnabledNative}, - {"enableNative", "()I", (void *)enableNative}, - {"disableNative", "()I", (void *)disableNative}, - - {"getAddressNative", "()Ljava/lang/String;", (void *)getAddressNative}, - {"getNameNative", "()Ljava/lang/String;", (void*)getNameNative}, - {"setNameNative", "(Ljava/lang/String;)Z", (void *)setNameNative}, - {"getVersionNative", "()Ljava/lang/String;", (void *)getVersionNative}, - {"getRevisionNative", "()Ljava/lang/String;", (void *)getRevisionNative}, - {"getManufacturerNative", "()Ljava/lang/String;", (void *)getManufacturerNative}, - {"getCompanyNative", "()Ljava/lang/String;", (void *)getCompanyNative}, - - {"getModeNative", "()Ljava/lang/String;", (void *)getModeNative}, - {"setModeNative", "(Ljava/lang/String;)Z", (void *)setModeNative}, - - {"getDiscoverableTimeoutNative", "()I", (void *)getDiscoverableTimeoutNative}, - {"setDiscoverableTimeoutNative", "(I)Z", (void *)setDiscoverableTimeoutNative}, - - {"startDiscoveryNative", "(Z)Z", (void*)startDiscoveryNative}, - {"cancelDiscoveryNative", "()Z", (void *)cancelDiscoveryNative}, - {"startPeriodicDiscoveryNative", "()Z", (void *)startPeriodicDiscoveryNative}, - {"stopPeriodicDiscoveryNative", "()Z", (void *)stopPeriodicDiscoveryNative}, - {"isPeriodicDiscoveryNative", "()Z", (void *)isPeriodicDiscoveryNative}, - {"listRemoteDevicesNative", "()[Ljava/lang/String;", (void *)listRemoteDevicesNative}, - - {"listConnectionsNative", "()[Ljava/lang/String;", (void *)listConnectionsNative}, - {"isConnectedNative", "(Ljava/lang/String;)Z", (void *)isConnectedNative}, - {"disconnectRemoteDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectRemoteDeviceNative}, - - {"createBondingNative", "(Ljava/lang/String;I)Z", (void *)createBondingNative}, - {"cancelBondingProcessNative", "(Ljava/lang/String;)Z", (void *)cancelBondingProcessNative}, - {"listBondingsNative", "()[Ljava/lang/String;", (void *)listBondingsNative}, - {"removeBondingNative", "(Ljava/lang/String;)Z", (void *)removeBondingNative}, - - {"getRemoteNameNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteNameNative}, - {"getRemoteVersionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteVersionNative}, - {"getRemoteRevisionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteRevisionNative}, - {"getRemoteClassNative", "(Ljava/lang/String;)I", (void *)getRemoteClassNative}, - {"getRemoteManufacturerNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteManufacturerNative}, - {"getRemoteCompanyNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteCompanyNative}, - {"getRemoteServiceChannelNative", "(Ljava/lang/String;S)Z", (void *)getRemoteServiceChannelNative}, - {"getRemoteFeaturesNative", "(Ljava/lang/String;)[B", (void *)getRemoteFeaturesNative}, - {"lastSeenNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastSeenNative}, - {"lastUsedNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastUsedNative}, - {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative}, - {"cancelPinNative", "(Ljava/lang/String;I)Z", (void *)cancelPinNative}, -}; - -int register_android_server_BluetoothDeviceService(JNIEnv *env) { - return AndroidRuntime::registerNativeMethods(env, - "android/server/BluetoothDeviceService", sMethods, NELEM(sMethods)); -} - -} /* namespace android */ diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index ad24136..d2e9454 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -36,36 +36,38 @@ namespace android { +#define CREATE_DEVICE_ALREADY_EXISTS 1 +#define CREATE_DEVICE_SUCCESS 0 +#define CREATE_DEVICE_FAILED -1 + #ifdef HAVE_BLUETOOTH static jfieldID field_mNativeData; -static jmethodID method_onModeChanged; -static jmethodID method_onNameChanged; -static jmethodID method_onDiscoveryStarted; -static jmethodID method_onDiscoveryCompleted; -static jmethodID method_onRemoteDeviceFound; -static jmethodID method_onRemoteDeviceDisappeared; -static jmethodID method_onRemoteClassUpdated; -static jmethodID method_onRemoteNameUpdated; -static jmethodID method_onRemoteNameFailed; -static jmethodID method_onRemoteDeviceConnected; -static jmethodID method_onRemoteDeviceDisconnectRequested; -static jmethodID method_onRemoteDeviceDisconnected; -static jmethodID method_onBondingCreated; -static jmethodID method_onBondingRemoved; - -static jmethodID method_onCreateBondingResult; -static jmethodID method_onGetRemoteServiceChannelResult; - -static jmethodID method_onPasskeyAgentRequest; -static jmethodID method_onPasskeyAgentCancel; -static jmethodID method_onAuthAgentAuthorize; -static jmethodID method_onAuthAgentCancel; - -static jmethodID method_onRestartRequired; +static jmethodID method_onPropertyChanged; +static jmethodID method_onDevicePropertyChanged; +static jmethodID method_onDeviceFound; +static jmethodID method_onDeviceDisappeared; +static jmethodID method_onDeviceCreated; +static jmethodID method_onDeviceRemoved; +static jmethodID method_onDeviceDisconnectRequested; + +static jmethodID method_onCreatePairedDeviceResult; +static jmethodID method_onCreateDeviceResult; +static jmethodID method_onDiscoverServicesResult; +static jmethodID method_onGetDeviceServiceChannelResult; + +static jmethodID method_onRequestPinCode; +static jmethodID method_onRequestPasskey; +static jmethodID method_onRequestPasskeyConfirmation; +static jmethodID method_onRequestPairingConsent; +static jmethodID method_onDisplayPasskey; +static jmethodID method_onAgentAuthorize; +static jmethodID method_onAgentCancel; typedef event_loop_native_data_t native_data_t; +#define EVENT_LOOP_REFS 10 + static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { return (native_data_t *)(env->GetIntField(object, field_mNativeData)); @@ -80,30 +82,40 @@ static void classInitNative(JNIEnv* env, jclass clazz) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH - method_onModeChanged = env->GetMethodID(clazz, "onModeChanged", "(Ljava/lang/String;)V"); - method_onNameChanged = env->GetMethodID(clazz, "onNameChanged", "(Ljava/lang/String;)V"); - method_onDiscoveryStarted = env->GetMethodID(clazz, "onDiscoveryStarted", "()V"); - method_onDiscoveryCompleted = env->GetMethodID(clazz, "onDiscoveryCompleted", "()V"); - method_onRemoteDeviceFound = env->GetMethodID(clazz, "onRemoteDeviceFound", "(Ljava/lang/String;IS)V"); - method_onRemoteDeviceDisappeared = env->GetMethodID(clazz, "onRemoteDeviceDisappeared", "(Ljava/lang/String;)V"); - method_onRemoteClassUpdated = env->GetMethodID(clazz, "onRemoteClassUpdated", "(Ljava/lang/String;I)V"); - method_onRemoteNameUpdated = env->GetMethodID(clazz, "onRemoteNameUpdated", "(Ljava/lang/String;Ljava/lang/String;)V"); - method_onRemoteNameFailed = env->GetMethodID(clazz, "onRemoteNameFailed", "(Ljava/lang/String;)V"); - method_onRemoteDeviceConnected = env->GetMethodID(clazz, "onRemoteDeviceConnected", "(Ljava/lang/String;)V"); - method_onRemoteDeviceDisconnectRequested = env->GetMethodID(clazz, "onRemoteDeviceDisconnectRequested", "(Ljava/lang/String;)V"); - method_onRemoteDeviceDisconnected = env->GetMethodID(clazz, "onRemoteDeviceDisconnected", "(Ljava/lang/String;)V"); - method_onBondingCreated = env->GetMethodID(clazz, "onBondingCreated", "(Ljava/lang/String;)V"); - method_onBondingRemoved = env->GetMethodID(clazz, "onBondingRemoved", "(Ljava/lang/String;)V"); - - method_onCreateBondingResult = env->GetMethodID(clazz, "onCreateBondingResult", "(Ljava/lang/String;I)V"); - - method_onPasskeyAgentRequest = env->GetMethodID(clazz, "onPasskeyAgentRequest", "(Ljava/lang/String;I)V"); - method_onPasskeyAgentCancel = env->GetMethodID(clazz, "onPasskeyAgentCancel", "(Ljava/lang/String;)V"); - method_onAuthAgentAuthorize = env->GetMethodID(clazz, "onAuthAgentAuthorize", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); - method_onAuthAgentCancel = env->GetMethodID(clazz, "onAuthAgentCancel", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - method_onGetRemoteServiceChannelResult = env->GetMethodID(clazz, "onGetRemoteServiceChannelResult", "(Ljava/lang/String;I)V"); - - method_onRestartRequired = env->GetMethodID(clazz, "onRestartRequired", "()V"); + method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged", + "([Ljava/lang/String;)V"); + method_onDevicePropertyChanged = env->GetMethodID(clazz, + "onDevicePropertyChanged", + "(Ljava/lang/String;[Ljava/lang/String;)V"); + method_onDeviceFound = env->GetMethodID(clazz, "onDeviceFound", + "(Ljava/lang/String;[Ljava/lang/String;)V"); + method_onDeviceDisappeared = env->GetMethodID(clazz, "onDeviceDisappeared", + "(Ljava/lang/String;)V"); + method_onDeviceCreated = env->GetMethodID(clazz, "onDeviceCreated", "(Ljava/lang/String;)V"); + method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V"); + method_onDeviceDisconnectRequested = env->GetMethodID(clazz, "onDeviceDisconnectRequested", + "(Ljava/lang/String;)V"); + + method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult", + "(Ljava/lang/String;I)V"); + method_onCreateDeviceResult = env->GetMethodID(clazz, "onCreateDeviceResult", + "(Ljava/lang/String;I)V"); + method_onDiscoverServicesResult = env->GetMethodID(clazz, "onDiscoverServicesResult", + "(Ljava/lang/String;Z)V"); + + method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize", + "(Ljava/lang/String;Ljava/lang/String;)Z"); + method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V"); + method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode", + "(Ljava/lang/String;I)V"); + method_onRequestPasskey = env->GetMethodID(clazz, "onRequestPasskey", + "(Ljava/lang/String;I)V"); + method_onRequestPasskeyConfirmation = env->GetMethodID(clazz, "onRequestPasskeyConfirmation", + "(Ljava/lang/String;II)V"); + method_onRequestPairingConsent = env->GetMethodID(clazz, "onRequestPairingConsent", + "(Ljava/lang/String;I)V"); + method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey", + "(Ljava/lang/String;II)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); #endif @@ -154,9 +166,11 @@ static void cleanupNativeDataNative(JNIEnv* env, jobject object) { #ifdef HAVE_BLUETOOTH static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, void *data); -static DBusHandlerResult agent_event_filter(DBusConnection *conn, - DBusMessage *msg, - void *data); +DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, + void *data); +static int register_agent(native_data_t *nat, + const char *agent_path, const char *capabilities); static const DBusObjectPathVTable agent_vtable = { NULL, agent_event_filter, NULL, NULL, NULL, NULL @@ -178,11 +192,12 @@ static short dbus_flags_to_unix_events(unsigned int flags) { static jboolean setUpEventLoop(native_data_t *nat) { LOGV(__FUNCTION__); - dbus_threads_init_default(); - DBusError err; - dbus_error_init(&err); if (nat != NULL && nat->conn != NULL) { + dbus_threads_init_default(); + DBusError err; + dbus_error_init(&err); + // Add a filter for all incoming messages if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){ return JNI_FALSE; @@ -204,108 +219,167 @@ static jboolean setUpEventLoop(native_data_t *nat) { return JNI_FALSE; } dbus_bus_add_match(nat->conn, - "type='signal',interface='org.bluez.audio.Manager'", + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } dbus_bus_add_match(nat->conn, - "type='signal',interface='org.bluez.audio.Device'", + "type='signal',interface='org.bluez.AudioSink'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } - dbus_bus_add_match(nat->conn, - "type='signal',interface='org.bluez.audio.Sink'", - &err); - if (dbus_error_is_set(&err)) { - LOG_AND_FREE_DBUS_ERROR(&err); + + const char *agent_path = "/android/bluetooth/agent"; + const char *capabilities = "DisplayYesNo"; + if (register_agent(nat, agent_path, capabilities) < 0) { + dbus_connection_unregister_object_path (nat->conn, agent_path); return JNI_FALSE; } + return JNI_TRUE; + } + return JNI_FALSE; +} - // Add an object handler for passkey agent method calls - const char *path = "/android/bluetooth/Agent"; - if (!dbus_connection_register_object_path(nat->conn, path, - &agent_vtable, nat)) { - LOGE("%s: Can't register object path %s for agent!", - __FUNCTION__, path); - return JNI_FALSE; + +const char * get_adapter_path(DBusConnection *conn) { + DBusMessage *msg = NULL, *reply = NULL; + DBusError err; + const char *device_path = NULL; + int attempt = 0; + + for (attempt = 0; attempt < 1000 && reply == NULL; attempt ++) { + msg = dbus_message_new_method_call("org.bluez", "/", + "org.bluez.Manager", "DefaultAdapter"); + if (!msg) { + LOGE("%s: Can't allocate new method call for get_adapter_path!", + __FUNCTION__); + return NULL; } + dbus_message_append_args(msg, DBUS_TYPE_INVALID); + dbus_error_init(&err); + reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); - // RegisterDefaultPasskeyAgent() will fail until hcid is up, so keep - // trying for 10 seconds. - int attempt; - for (attempt = 0; attempt < 1000; attempt++) { - DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err, - BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "RegisterDefaultPasskeyAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (reply) { - // Success - dbus_message_unref(reply); - LOGV("Registered agent on attempt %d of 1000\n", attempt); - break; - } else if (dbus_error_has_name(&err, + if (!reply) { + if (dbus_error_is_set(&err)) { + if (dbus_error_has_name(&err, "org.freedesktop.DBus.Error.ServiceUnknown")) { - // hcid is still down, retry - dbus_error_free(&err); - usleep(10000); // 10 ms - } else { - // Some other error we weren't expecting - LOG_AND_FREE_DBUS_ERROR(&err); - return JNI_FALSE; + // bluetoothd is still down, retry + LOG_AND_FREE_DBUS_ERROR(&err); + usleep(10000); // 10 ms + continue; + } else { + // Some other error we weren't expecting + LOG_AND_FREE_DBUS_ERROR(&err); + } } + goto failed; } - if (attempt == 1000) { - LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), " - "is hcid running?"); - return JNI_FALSE; - } + } + if (attempt == 1000) { + LOGE("Time out while trying to get Adapter path, is bluetoothd up ?"); + goto failed; + } - // Now register the Auth agent - DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err, - BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "RegisterDefaultAuthorizationAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (!reply) { + if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, + &device_path, DBUS_TYPE_INVALID) + || !device_path){ + if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); - return JNI_FALSE; } + goto failed; + } + dbus_message_unref(msg); + return device_path; - dbus_message_unref(reply); - return JNI_TRUE; +failed: + dbus_message_unref(msg); + return NULL; +} + +static int register_agent(native_data_t *nat, + const char * agent_path, const char * capabilities) +{ + DBusMessage *msg, *reply; + DBusError err; + + if (!dbus_connection_register_object_path(nat->conn, agent_path, + &agent_vtable, nat)) { + LOGE("%s: Can't register object path %s for agent!", + __FUNCTION__, agent_path); + return -1; } - return JNI_FALSE; + nat->adapter = get_adapter_path(nat->conn); + if (nat->adapter == NULL) { + return -1; + } + msg = dbus_message_new_method_call("org.bluez", nat->adapter, + "org.bluez.Adapter", "RegisterAgent"); + if (!msg) { + LOGE("%s: Can't allocate new method call for agent!", + __FUNCTION__); + return -1; + } + dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path, + DBUS_TYPE_STRING, &capabilities, + DBUS_TYPE_INVALID); + + dbus_error_init(&err); + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + dbus_message_unref(msg); + + if (!reply) { + LOGE("%s: Can't register agent!", __FUNCTION__); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } + return -1; + } + + dbus_message_unref(reply); + dbus_connection_flush(nat->conn); + + return 0; } static void tearDownEventLoop(native_data_t *nat) { LOGV(__FUNCTION__); if (nat != NULL && nat->conn != NULL) { + DBusMessage *msg, *reply; DBusError err; dbus_error_init(&err); + const char * agent_path = "/android/bluetooth/agent"; + + msg = dbus_message_new_method_call("org.bluez", + nat->adapter, + "org.bluez.Adapter", + "UnregisterAgent"); + if (msg != NULL) { + dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path, + DBUS_TYPE_INVALID); + reply = dbus_connection_send_with_reply_and_block(nat->conn, + msg, -1, &err); - const char *path = "/android/bluetooth/Agent"; - DBusMessage *reply = - dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "UnregisterDefaultPasskeyAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (reply) dbus_message_unref(reply); - - reply = - dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "UnregisterDefaultAuthorizationAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (reply) dbus_message_unref(reply); + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + dbus_error_free(&err); + } + } else { + dbus_message_unref(reply); + } + dbus_message_unref(msg); + } else { + LOGE("%s: Can't create new method call!", __FUNCTION__); + } - dbus_connection_unregister_object_path(nat->conn, path); + dbus_connection_flush(nat->conn); + dbus_connection_unregister_object_path(nat->conn, agent_path); dbus_bus_remove_match(nat->conn, "type='signal',interface='org.bluez.audio.Sink'", @@ -650,6 +724,7 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, native_data_t *nat; JNIEnv *env; DBusError err; + DBusHandlerResult ret; dbus_error_init(&err); @@ -660,321 +735,178 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - LOGV("%s: Received signal %s:%s from %s", __FUNCTION__, - dbus_message_get_interface(msg), dbus_message_get_member(msg), - dbus_message_get_path(msg)); + LOGE("%s: Received signal %s:%s from %s", __FUNCTION__, + dbus_message_get_interface(msg), dbus_message_get_member(msg), + dbus_message_get_path(msg)); + env->PushLocalFrame(EVENT_LOOP_REFS); if (dbus_message_is_signal(msg, "org.bluez.Adapter", - "RemoteDeviceFound")) { + "DeviceFound")) { char *c_address; - int n_class; - short n_rssi; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_UINT32, &n_class, - DBUS_TYPE_INT16, &n_rssi, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s class = %#X rssi = %hd", c_address, n_class, - n_rssi); - env->CallVoidMethod(nat->me, - method_onRemoteDeviceFound, - env->NewStringUTF(c_address), - (jint)n_class, - (jshort)n_rssi); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "DiscoveryStarted")) { - LOGI("DiscoveryStarted signal received"); - env->CallVoidMethod(nat->me, method_onDiscoveryStarted); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "DiscoveryCompleted")) { - LOGI("DiscoveryCompleted signal received"); - env->CallVoidMethod(nat->me, method_onDiscoveryCompleted); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteDeviceDisappeared")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, method_onRemoteDeviceDisappeared, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteClassUpdated")) { - char *c_address; - int n_class; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_UINT32, &n_class, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, method_onRemoteClassUpdated, - env->NewStringUTF(c_address), (jint)n_class); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteNameUpdated")) { - char *c_address; - char *c_name; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_STRING, &c_name, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s, name = %s", c_address, c_name); + DBusMessageIter iter; + jobjectArray str_array = NULL; + if (dbus_message_iter_init(msg, &iter)) { + dbus_message_iter_get_basic(&iter, &c_address); + if (dbus_message_iter_next(&iter)) + str_array = + parse_remote_device_properties(env, &iter); + } + if (str_array != NULL) { env->CallVoidMethod(nat->me, - method_onRemoteNameUpdated, + method_onDeviceFound, env->NewStringUTF(c_address), - env->NewStringUTF(c_name)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; + str_array); + } else + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + goto success; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteNameFailed")) { + "org.bluez.Adapter", + "DeviceDisappeared")) { char *c_address; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_INVALID)) { LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onRemoteNameFailed, + env->CallVoidMethod(nat->me, method_onDeviceDisappeared, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; + goto success; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteDeviceConnected")) { - char *c_address; + "org.bluez.Adapter", + "DeviceCreated")) { + char *c_object_path; if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_OBJECT_PATH, &c_object_path, DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); + LOGV("... address = %s", c_object_path); env->CallVoidMethod(nat->me, - method_onRemoteDeviceConnected, - env->NewStringUTF(c_address)); + method_onDeviceCreated, + env->NewStringUTF(c_object_path)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; + goto success; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteDeviceDisconnectRequested")) { - char *c_address; + "org.bluez.Adapter", + "DeviceRemoved")) { + char *c_object_path; if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onRemoteDeviceDisconnectRequested, - env->NewStringUTF(c_address)); + DBUS_TYPE_OBJECT_PATH, &c_object_path, + DBUS_TYPE_INVALID)) { + LOGV("... Object Path = %s", c_object_path); + env->CallVoidMethod(nat->me, + method_onDeviceRemoved, + env->NewStringUTF(c_object_path)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; + goto success; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", - "RemoteDeviceDisconnected")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onRemoteDeviceDisconnected, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "BondingCreated")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onBondingCreated, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "BondingRemoved")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onBondingRemoved, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "ModeChanged")) { - char *c_mode; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_mode, - DBUS_TYPE_INVALID)) { - LOGV("... mode = %s", c_mode); + "PropertyChanged")) { + jobjectArray str_array = parse_adapter_property_change(env, msg); + if (str_array != NULL) { + /* Check if bluetoothd has (re)started, if so update the path. */ + jstring property =(jstring) env->GetObjectArrayElement(str_array, 0); + const char *c_property = env->GetStringUTFChars(property, NULL); + if (!strncmp(c_property, "Powered", strlen("Powered"))) { + jstring value = + (jstring) env->GetObjectArrayElement(str_array, 1); + const char *c_value = env->GetStringUTFChars(value, NULL); + if (!strncmp(c_value, "true", strlen("true"))) + nat->adapter = get_adapter_path(nat->conn); + env->ReleaseStringUTFChars(value, c_value); + } + env->ReleaseStringUTFChars(property, c_property); + env->CallVoidMethod(nat->me, - method_onModeChanged, - env->NewStringUTF(c_mode)); + method_onPropertyChanged, + str_array); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; + goto success; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "NameChanged")) { - char *c_name; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_name, - DBUS_TYPE_INVALID)) { - LOGV("... name = %s", c_name); + "org.bluez.Device", + "PropertyChanged")) { + jobjectArray str_array = parse_remote_device_property_change(env, msg); + if (str_array != NULL) { + const char *remote_device_path = dbus_message_get_path(msg); env->CallVoidMethod(nat->me, - method_onNameChanged, - env->NewStringUTF(c_name)); + method_onDevicePropertyChanged, + env->NewStringUTF(remote_device_path), + str_array); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; + goto success; } else if (dbus_message_is_signal(msg, - "org.freedesktop.DBus", - "NameOwnerChanged")) { - char *c_name; - char *c_old_owner; - char *c_new_owner; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_name, - DBUS_TYPE_STRING, &c_old_owner, - DBUS_TYPE_STRING, &c_new_owner, - DBUS_TYPE_INVALID)) { - LOGV("... name = %s", c_name); - LOGV("... old_owner = %s", c_old_owner); - LOGV("... new_owner = %s", c_new_owner); - if (!strcmp(c_name, "org.bluez") && c_new_owner[0] != '\0') { - // New owner of org.bluez. This can only happen when hcid - // restarts. Need to restart framework BT services to recover. - LOGE("Looks like hcid restarted"); - env->CallVoidMethod(nat->me, method_onRestartRequired); - } - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; + "org.bluez.Device", + "DisconnectRequested")) { + const char *remote_device_path = dbus_message_get_path(msg); + env->CallVoidMethod(nat->me, + method_onDeviceDisconnectRequested, + env->NewStringUTF(remote_device_path)); + goto success; } - return a2dp_event_filter(msg, env); + ret = a2dp_event_filter(msg, env); + env->PopLocalFrame(NULL); + return ret; + +success: + env->PopLocalFrame(NULL); + return DBUS_HANDLER_RESULT_HANDLED; } // Called by dbus during WaitForAndDispatchEventNative() -static DBusHandlerResult agent_event_filter(DBusConnection *conn, - DBusMessage *msg, void *data) { +DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, void *data) { native_data_t *nat = (native_data_t *)data; JNIEnv *env; - nat->vm->GetEnv((void**)&env, nat->envVer); if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) { LOGV("%s: not interested (not a method call).", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - LOGV("%s: Received method %s:%s", __FUNCTION__, + LOGI("%s: Received method %s:%s", __FUNCTION__, dbus_message_get_interface(msg), dbus_message_get_member(msg)); - if (dbus_message_is_method_call(msg, - "org.bluez.PasskeyAgent", "Request")) { + if (nat == NULL) return DBUS_HANDLER_RESULT_HANDLED; - const char *adapter; - const char *address; - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID)) { - LOGE("%s: Invalid arguments for Request() method", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - LOGV("... address = %s", address); - - dbus_message_ref(msg); // increment refcount because we pass to java - - env->CallVoidMethod(nat->me, method_onPasskeyAgentRequest, - env->NewStringUTF(address), (int)msg); - - return DBUS_HANDLER_RESULT_HANDLED; - - } else if (dbus_message_is_method_call(msg, - "org.bluez.PasskeyAgent", "Cancel")) { - - const char *adapter; - const char *address; - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID)) { - LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - LOGV("... address = %s", address); - - env->CallVoidMethod(nat->me, method_onPasskeyAgentCancel, - env->NewStringUTF(address)); + nat->vm->GetEnv((void**)&env, nat->envVer); + env->PushLocalFrame(EVENT_LOOP_REFS); + if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "Cancel")) { + env->CallVoidMethod(nat->me, method_onAgentCancel); // reply DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { LOGE("%s: Cannot create message reply\n", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto failure; } dbus_connection_send(nat->conn, reply, NULL); dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; + goto success; } else if (dbus_message_is_method_call(msg, - "org.bluez.PasskeyAgent", "Release")) { - LOGW("We are no longer the passkey agent!"); - - // reply - DBusMessage *reply = dbus_message_new_method_return(msg); - if (!reply) { - LOGE("%s: Cannot create message reply\n", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - dbus_connection_send(nat->conn, reply, NULL); - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_method_call(msg, - "org.bluez.AuthorizationAgent", "Authorize")) { - const char *adapter; - const char *address; - const char *service; + "org.bluez.Agent", "Authorize")) { + char *object_path; const char *uuid; if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &service, + DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID)) { LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto failure; } - LOGV("... address = %s", address); - LOGV("... service = %s", service); + LOGV("... object_path = %s", object_path); LOGV("... uuid = %s", uuid); - bool auth_granted = env->CallBooleanMethod(nat->me, - method_onAuthAgentAuthorize, env->NewStringUTF(address), - env->NewStringUTF(service), env->NewStringUTF(uuid)); + bool auth_granted = + env->CallBooleanMethod(nat->me, method_onAgentAuthorize, + env->NewStringUTF(object_path), env->NewStringUTF(uuid)); // reply if (auth_granted) { DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { LOGE("%s: Cannot create message reply\n", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto failure; } dbus_connection_send(nat->conn, reply, NULL); dbus_message_unref(reply); @@ -983,64 +915,116 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn, "org.bluez.Error.Rejected", "Authorization rejected"); if (!reply) { LOGE("%s: Cannot create message reply\n", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto failure; } dbus_connection_send(nat->conn, reply, NULL); dbus_message_unref(reply); } - return DBUS_HANDLER_RESULT_HANDLED; + goto success; } else if (dbus_message_is_method_call(msg, - "org.bluez.AuthorizationAgent", "Cancel")) { - const char *adapter; - const char *address; - const char *service; - const char *uuid; + "org.bluez.Agent", "RequestPinCode")) { + char *object_path; if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &service, - DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)) { - LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + LOGE("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__); + goto failure; } - LOGV("... address = %s", address); - LOGV("... service = %s", service); - LOGV("... uuid = %s", uuid); + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onRequestPinCode, + env->NewStringUTF(object_path), + int(msg)); + goto success; + } else if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "RequestPasskey")) { + char *object_path; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__); + goto failure; + } - env->CallVoidMethod(nat->me, - method_onAuthAgentCancel, env->NewStringUTF(address), - env->NewStringUTF(service), env->NewStringUTF(uuid)); + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onRequestPasskey, + env->NewStringUTF(object_path), + int(msg)); + goto success; + } else if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "DisplayPasskey")) { + char *object_path; + uint32_t passkey; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_UINT32, &passkey, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__); + goto failure; + } - // reply - DBusMessage *reply = dbus_message_new_method_return(msg); - if (!reply) { - LOGE("%s: Cannot create message reply\n", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onDisplayPasskey, + env->NewStringUTF(object_path), + passkey, + int(msg)); + goto success; + } else if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "RequestConfirmation")) { + char *object_path; + uint32_t passkey; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_UINT32, &passkey, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for RequestConfirmation() method", __FUNCTION__); + goto failure; } - dbus_connection_send(nat->conn, reply, NULL); - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onRequestPasskeyConfirmation, + env->NewStringUTF(object_path), + passkey, + int(msg)); + goto success; } else if (dbus_message_is_method_call(msg, - "org.bluez.AuthorizationAgent", "Release")) { - LOGW("We are no longer the auth agent!"); + "org.bluez.Agent", "RequestPairingConsent")) { + char *object_path; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for RequestPairingConsent() method", __FUNCTION__); + goto failure; + } + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onRequestPairingConsent, + env->NewStringUTF(object_path), + int(msg)); + goto success; + } else if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "Release")) { // reply DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { LOGE("%s: Cannot create message reply\n", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto failure; } dbus_connection_send(nat->conn, reply, NULL); dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; + goto success; } else { - LOGV("... ignored"); + LOGV("%s:%s is ignored", dbus_message_get_interface(msg), dbus_message_get_member(msg)); } +failure: + env->PopLocalFrame(NULL); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +success: + env->PopLocalFrame(NULL); + return DBUS_HANDLER_RESULT_HANDLED; + } #endif @@ -1054,8 +1038,10 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn, #define BOND_RESULT_AUTH_CANCELED 3 #define BOND_RESULT_REMOTE_DEVICE_DOWN 4 #define BOND_RESULT_DISCOVERY_IN_PROGRESS 5 +#define BOND_RESULT_AUTH_TIMEOUT 6 +#define BOND_RESULT_REPEATED_ATTEMPTS 7 -void onCreateBondingResult(DBusMessage *msg, void *user, void *n) { +void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) { LOGV(__FUNCTION__); native_data_t *nat = (native_data_t *)n; @@ -1099,6 +1085,12 @@ void onCreateBondingResult(DBusMessage *msg, void *user, void *n) { !strcmp(err.message, "Discover in progress")) { LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_DISCOVERY_IN_PROGRESS; + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.RepeatedAttempts")) { + LOGV("... error = %s (%s)\n", err.name, err.message); + result = BOND_RESULT_REPEATED_ATTEMPTS; + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationTimeout")) { + LOGV("... error = %s (%s)\n", err.name, err.message); + result = BOND_RESULT_AUTH_TIMEOUT; } else { LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); result = BOND_RESULT_ERROR; @@ -1106,7 +1098,7 @@ void onCreateBondingResult(DBusMessage *msg, void *user, void *n) { } env->CallVoidMethod(nat->me, - method_onCreateBondingResult, + method_onCreatePairedDeviceResult, env->NewStringUTF(address), result); done: @@ -1114,7 +1106,58 @@ done: free(user); } -void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) { +void onCreateDeviceResult(DBusMessage *msg, void *user, void *n) { + LOGV(__FUNCTION__); + + native_data_t *nat = (native_data_t *)n; + const char *address= (const char *)user; + DBusError err; + dbus_error_init(&err); + JNIEnv *env; + nat->vm->GetEnv((void**)&env, nat->envVer); + + LOGV("... Address = %s", address); + + jint result = CREATE_DEVICE_SUCCESS; + if (dbus_set_error_from_message(&err, msg)) { + if (dbus_error_has_name(&err, "org.bluez.Error.AlreadyExists")) { + result = CREATE_DEVICE_ALREADY_EXISTS; + } + LOG_AND_FREE_DBUS_ERROR(&err); + result = CREATE_DEVICE_FAILED; + } + env->CallVoidMethod(nat->me, + method_onCreateDeviceResult, + env->NewStringUTF(address), + result); + free(user); +} + +void onDiscoverServicesResult(DBusMessage *msg, void *user, void *n) { + LOGV(__FUNCTION__); + + native_data_t *nat = (native_data_t *)n; + const char *path = (const char *)user; + DBusError err; + dbus_error_init(&err); + JNIEnv *env; + nat->vm->GetEnv((void**)&env, nat->envVer); + + LOGV("... Device Path = %s", path); + + bool result = JNI_TRUE; + if (dbus_set_error_from_message(&err, msg)) { + LOG_AND_FREE_DBUS_ERROR(&err); + result = JNI_FALSE; + } + env->CallVoidMethod(nat->me, + method_onDiscoverServicesResult, + env->NewStringUTF(path), + result); + free(user); +} + +void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) { LOGV(__FUNCTION__); const char *address = (const char *) user; @@ -1133,14 +1176,13 @@ void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) { !dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &channel, DBUS_TYPE_INVALID)) { - /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */ LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); dbus_error_free(&err); } done: env->CallVoidMethod(nat->me, - method_onGetRemoteServiceChannelResult, + method_onGetDeviceServiceChannelResult, env->NewStringUTF(address), channel); free(user); diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp new file mode 100644 index 0000000..ea64305 --- /dev/null +++ b/core/jni/android_server_BluetoothService.cpp @@ -0,0 +1,915 @@ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter" +#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device" +#define LOG_TAG "BluetoothService.cpp" + +#include "android_bluetooth_common.h" +#include "android_runtime/AndroidRuntime.h" +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef HAVE_BLUETOOTH +#include +#include +#endif + +#include + +namespace android { + +#define BLUETOOTH_CLASS_ERROR 0xFF000000 +#define PROPERTIES_NREFS 10 + +#ifdef HAVE_BLUETOOTH +// We initialize these variables when we load class +// android.server.BluetoothService +static jfieldID field_mNativeData; +static jfieldID field_mEventLoop; + +typedef struct { + JNIEnv *env; + DBusConnection *conn; + const char *adapter; // dbus object name of the local adapter +} native_data_t; + +extern event_loop_native_data_t *get_EventLoop_native_data(JNIEnv *, + jobject); +extern DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, + void *data); +void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat); +void onDiscoverServicesResult(DBusMessage *msg, void *user, void *nat); +void onCreateDeviceResult(DBusMessage *msg, void *user, void *nat); + + +/** Get native data stored in the opaque (Java code maintained) pointer mNativeData + * Perform quick sanity check, if there are any problems return NULL + */ +static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { + native_data_t *nat = + (native_data_t *)(env->GetIntField(object, field_mNativeData)); + if (nat == NULL || nat->conn == NULL) { + LOGE("Uninitialized native data\n"); + return NULL; + } + return nat; +} +#endif + +static void classInitNative(JNIEnv* env, jclass clazz) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + field_mNativeData = get_field(env, clazz, "mNativeData", "I"); + field_mEventLoop = get_field(env, clazz, "mEventLoop", + "Landroid/server/BluetoothEventLoop;"); +#endif +} + +/* Returns true on success (even if adapter is present but disabled). + * Return false if dbus is down, or another serious error (out of memory) +*/ +static bool initializeNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t)); + if (NULL == nat) { + LOGE("%s: out of memory!", __FUNCTION__); + return false; + } + nat->env = env; + + env->SetIntField(object, field_mNativeData, (jint)nat); + DBusError err; + dbus_error_init(&err); + dbus_threads_init_default(); + nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (dbus_error_is_set(&err)) { + LOGE("Could not get onto the system bus: %s", err.message); + dbus_error_free(&err); + return false; + } + dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); +#endif /*HAVE_BLUETOOTH*/ + return true; +} + +static const char *get_adapter_path(JNIEnv* env, jobject object) { +#ifdef HAVE_BLUETOOTH + event_loop_native_data_t *event_nat = + get_EventLoop_native_data(env, env->GetObjectField(object, + field_mEventLoop)); + if (event_nat == NULL) + return NULL; + return event_nat->adapter; +#else + return NULL; +#endif +} + +// This function is called when the adapter is enabled. +static jboolean setupNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = + (native_data_t *)env->GetIntField(object, field_mNativeData); + event_loop_native_data_t *event_nat = + get_EventLoop_native_data(env, env->GetObjectField(object, + field_mEventLoop)); + // Register agent for remote devices. + const char *device_agent_path = "/android/bluetooth/remote_device_agent"; + static const DBusObjectPathVTable agent_vtable = { + NULL, agent_event_filter, NULL, NULL, NULL, NULL }; + + if (!dbus_connection_register_object_path(nat->conn, device_agent_path, + &agent_vtable, event_nat)) { + LOGE("%s: Can't register object path %s for remote device agent!", + __FUNCTION__, device_agent_path); + return JNI_FALSE; + } +#endif /*HAVE_BLUETOOTH*/ + return JNI_TRUE; +} + +static jboolean tearDownNativeDataNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = + (native_data_t *)env->GetIntField(object, field_mNativeData); + if (nat != NULL) { + const char *device_agent_path = + "/android/bluetooth/remote_device_agent"; + dbus_connection_unregister_object_path (nat->conn, device_agent_path); + } +#endif /*HAVE_BLUETOOTH*/ + return JNI_TRUE; +} + +static void cleanupNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = + (native_data_t *)env->GetIntField(object, field_mNativeData); + if (nat) { + free(nat); + nat = NULL; + } +#endif +} + +static jstring getAdapterPathNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + return (env->NewStringUTF(get_adapter_path(env, object))); + } +#endif + return NULL; +} + + +static jboolean startDiscoveryNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + DBusError err; + const char *name; + jboolean ret = JNI_FALSE; + + native_data_t *nat = get_native_data(env, object); + if (nat == NULL) { + goto done; + } + + dbus_error_init(&err); + + /* Compose the command */ + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "StartDiscovery"); + + if (msg == NULL) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + } + goto done; + } + + /* Send the command. */ + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + ret = JNI_FALSE; + goto done; + } + + ret = JNI_TRUE; +done: + if (reply) dbus_message_unref(reply); + if (msg) dbus_message_unref(msg); + return ret; +#else + return JNI_FALSE; +#endif +} + +static void stopDiscoveryNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + DBusError err; + const char *name; + jstring ret; + native_data_t *nat; + + dbus_error_init(&err); + + nat = get_native_data(env, object); + if (nat == NULL) { + goto done; + } + + /* Compose the command */ + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "StopDiscovery"); + if (msg == NULL) { + if (dbus_error_is_set(&err)) + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + goto done; + } + + /* Send the command. */ + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + if (dbus_error_is_set(&err)) { + if(strncmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized", + strlen(BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized")) == 0) { + // hcid sends this if there is no active discovery to cancel + LOGV("%s: There was no active discovery to cancel", __FUNCTION__); + dbus_error_free(&err); + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + } + } + +done: + if (msg) dbus_message_unref(msg); + if (reply) dbus_message_unref(reply); +#endif +} + +static jboolean createPairedDeviceNative(JNIEnv *env, jobject object, + jstring address, jint timeout_ms) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + LOGV("... address = %s", c_address); + char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); + const char *capabilities = "DisplayYesNo"; + const char *agent_path = "/android/bluetooth/remote_device_agent"; + + strlcpy(context_address, c_address, BTADDR_SIZE); // for callback + bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms, + onCreatePairedDeviceResult, // callback + context_address, + eventLoopNat, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, + "CreatePairedDevice", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_OBJECT_PATH, &agent_path, + DBUS_TYPE_STRING, &capabilities, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + return ret ? JNI_TRUE : JNI_FALSE; + + } +#endif + return JNI_FALSE; +} + +static jint getDeviceServiceChannelNative(JNIEnv *env, jobject object, + jstring path, + jstring pattern, jint attr_id) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + if (nat && eventLoopNat) { + const char *c_pattern = env->GetStringUTFChars(pattern, NULL); + const char *c_path = env->GetStringUTFChars(path, NULL); + LOGV("... pattern = %s", c_pattern); + LOGV("... attr_id = %#X", attr_id); + DBusMessage *reply = + dbus_func_args(env, nat->conn, c_path, + DBUS_DEVICE_IFACE, "GetServiceAttributeValue", + DBUS_TYPE_STRING, &c_pattern, + DBUS_TYPE_UINT16, &attr_id, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(pattern, c_pattern); + env->ReleaseStringUTFChars(path, c_path); + return reply ? dbus_returns_int32(env, reply) : -1; + } +#endif + return -1; +} + +static jboolean cancelDeviceCreationNative(JNIEnv *env, jobject object, + jstring address) { + LOGV(__FUNCTION__); + jboolean result = JNI_FALSE; +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + DBusError err; + dbus_error_init(&err); + LOGV("... address = %s", c_address); + DBusMessage *reply = + dbus_func_args_timeout(env, nat->conn, -1, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "CancelDeviceCreation", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return JNI_FALSE; + } else { + result = JNI_TRUE; + } + dbus_message_unref(reply); + } +#endif + return JNI_FALSE; +} + +static jboolean removeDeviceNative(JNIEnv *env, jobject object, jstring object_path) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_object_path = env->GetStringUTFChars(object_path, NULL); + bool ret = dbus_func_args_async(env, nat->conn, -1, + NULL, + NULL, + NULL, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, + "RemoveDevice", + DBUS_TYPE_OBJECT_PATH, &c_object_path, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(object_path, c_object_path); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jint enableNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + return bt_enable(); +#endif + return -1; +} + +static jint disableNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + return bt_disable(); +#endif + return -1; +} + +static jint isEnabledNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + return bt_is_enabled(); +#endif + return -1; +} + +static jboolean setPairingConfirmationNative(JNIEnv *env, jobject object, + jstring address, bool confirm, + int nativeData) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg = (DBusMessage *)nativeData; + DBusMessage *reply; + if (confirm) { + reply = dbus_message_new_method_return(msg); + } else { + reply = dbus_message_new_error(msg, + "org.bluez.Error.Rejected", "User rejected confirmation"); + } + + if (!reply) { + LOGE("%s: Cannot create message reply to RequestPasskeyConfirmation or" + "RequestPairingConsent to D-Bus\n", __FUNCTION__); + dbus_message_unref(msg); + return JNI_FALSE; + } + + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(msg); + dbus_message_unref(reply); + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jboolean setPasskeyNative(JNIEnv *env, jobject object, jstring address, + int passkey, int nativeData) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg = (DBusMessage *)nativeData; + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply to return Passkey code to " + "D-Bus\n", __FUNCTION__); + dbus_message_unref(msg); + return JNI_FALSE; + } + + dbus_message_append_args(reply, DBUS_TYPE_UINT32, (uint32_t *)&passkey, + DBUS_TYPE_INVALID); + + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(msg); + dbus_message_unref(reply); + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jboolean setPinNative(JNIEnv *env, jobject object, jstring address, + jstring pin, int nativeData) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg = (DBusMessage *)nativeData; + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply to return PIN code to " + "D-Bus\n", __FUNCTION__); + dbus_message_unref(msg); + return JNI_FALSE; + } + + const char *c_pin = env->GetStringUTFChars(pin, NULL); + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &c_pin, + DBUS_TYPE_INVALID); + + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(msg); + dbus_message_unref(reply); + env->ReleaseStringUTFChars(pin, c_pin); + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jboolean cancelPairingUserInputNative(JNIEnv *env, jobject object, + jstring address, int nativeData) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg = (DBusMessage *)nativeData; + DBusMessage *reply = dbus_message_new_error(msg, + "org.bluez.Error.Canceled", "Pairing User Input was canceled"); + if (!reply) { + LOGE("%s: Cannot create message reply to return cancelUserInput to" + "D-BUS\n", __FUNCTION__); + dbus_message_unref(msg); + return JNI_FALSE; + } + + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(msg); + dbus_message_unref(reply); + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jobjectArray getDevicePropertiesNative(JNIEnv *env, jobject object, + jstring path) +{ +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg, *reply; + DBusError err; + dbus_error_init(&err); + + const char *c_path = env->GetStringUTFChars(path, NULL); + reply = dbus_func_args_timeout(env, + nat->conn, -1, c_path, + DBUS_DEVICE_IFACE, "GetProperties", + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(path, c_path); + + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return NULL; + } + env->PushLocalFrame(PROPERTIES_NREFS); + + DBusMessageIter iter; + jobjectArray str_array = NULL; + if (dbus_message_iter_init(reply, &iter)) + str_array = parse_remote_device_properties(env, &iter); + dbus_message_unref(reply); + + env->PopLocalFrame(NULL); + + return str_array; + } +#endif + return NULL; +} + +static jobjectArray getAdapterPropertiesNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg, *reply; + DBusError err; + dbus_error_init(&err); + + reply = dbus_func_args_timeout(env, + nat->conn, -1, get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "GetProperties", + DBUS_TYPE_INVALID); + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return NULL; + } + env->PushLocalFrame(PROPERTIES_NREFS); + + DBusMessageIter iter; + jobjectArray str_array = NULL; + if (dbus_message_iter_init(reply, &iter)) + str_array = parse_adapter_properties(env, &iter); + dbus_message_unref(reply); + + env->PopLocalFrame(NULL); + return str_array; + } +#endif + return NULL; +} + +static jboolean setAdapterPropertyNative(JNIEnv *env, jobject object, jstring key, + void *value, jint type) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply, *msg; + DBusMessageIter iter; + DBusError err; + const char *c_key = env->GetStringUTFChars(key, NULL); + dbus_error_init(&err); + + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "SetProperty"); + if (!msg) { + LOGE("%s: Can't allocate new method call for GetProperties!", + __FUNCTION__); + env->ReleaseStringUTFChars(key, c_key); + return JNI_FALSE; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID); + dbus_message_iter_init_append(msg, &iter); + append_variant(&iter, type, value); + + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + dbus_message_unref(msg); + + env->ReleaseStringUTFChars(key, c_key); + + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return JNI_FALSE; + } + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jboolean setAdapterPropertyStringNative(JNIEnv *env, jobject object, jstring key, + jstring value) { +#ifdef HAVE_BLUETOOTH + const char *c_value = env->GetStringUTFChars(value, NULL); + jboolean ret = setAdapterPropertyNative(env, object, key, (void *)&c_value, DBUS_TYPE_STRING); + env->ReleaseStringUTFChars(value, (char *)c_value); + return ret; +#else + return JNI_FALSE; +#endif +} + +static jboolean setAdapterPropertyIntegerNative(JNIEnv *env, jobject object, jstring key, + jint value) { +#ifdef HAVE_BLUETOOTH + return setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_UINT32); +#else + return JNI_FALSE; +#endif +} + +static jboolean setAdapterPropertyBooleanNative(JNIEnv *env, jobject object, jstring key, + jint value) { +#ifdef HAVE_BLUETOOTH + return setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_BOOLEAN); +#else + return JNI_FALSE; +#endif +} + +static jboolean setDevicePropertyNative(JNIEnv *env, jobject object, jstring path, + jstring key, void *value, jint type) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply, *msg; + DBusMessageIter iter; + DBusError err; + + const char *c_key = env->GetStringUTFChars(key, NULL); + const char *c_path = env->GetStringUTFChars(path, NULL); + + dbus_error_init(&err); + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, + c_path, DBUS_DEVICE_IFACE, "SetProperty"); + if (!msg) { + LOGE("%s: Can't allocate new method call for device SetProperty!", __FUNCTION__); + env->ReleaseStringUTFChars(key, c_key); + env->ReleaseStringUTFChars(path, c_path); + return JNI_FALSE; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID); + dbus_message_iter_init_append(msg, &iter); + append_variant(&iter, type, value); + + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + dbus_message_unref(msg); + + env->ReleaseStringUTFChars(key, c_key); + env->ReleaseStringUTFChars(path, c_path); + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return JNI_FALSE; + } + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jboolean setDevicePropertyBooleanNative(JNIEnv *env, jobject object, + jstring path, jstring key, jint value) { +#ifdef HAVE_BLUETOOTH + return setDevicePropertyNative(env, object, path, key, + (void *)&value, DBUS_TYPE_BOOLEAN); +#else + return JNI_FALSE; +#endif +} + + +static jboolean createDeviceNative(JNIEnv *env, jobject object, + jstring address) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + LOGV("... address = %s", c_address); + char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); + strlcpy(context_address, c_address, BTADDR_SIZE); // for callback + + bool ret = dbus_func_args_async(env, nat->conn, -1, + onCreateDeviceResult, + context_address, + eventLoopNat, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, + "CreateDevice", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jboolean discoverServicesNative(JNIEnv *env, jobject object, + jstring path, jstring pattern) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { + const char *c_path = env->GetStringUTFChars(path, NULL); + const char *c_pattern = env->GetStringUTFChars(pattern, NULL); + int len = env->GetStringLength(path) + 1; + char *context_path = (char *)calloc(len, sizeof(char)); + strlcpy(context_path, c_path, len); // for callback + + LOGV("... Object Path = %s", c_path); + LOGV("... Pattern = %s, strlen = %d", c_pattern, strlen(c_pattern)); + + bool ret = dbus_func_args_async(env, nat->conn, -1, + onDiscoverServicesResult, + context_path, + eventLoopNat, + c_path, + DBUS_DEVICE_IFACE, + "DiscoverServices", + DBUS_TYPE_STRING, &c_pattern, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(path, c_path); + env->ReleaseStringUTFChars(pattern, c_pattern); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jint addRfcommServiceRecordNative(JNIEnv *env, jobject object, + jstring name, jlong uuidMsb, jlong uuidLsb, jshort channel) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_name = env->GetStringUTFChars(name, NULL); + LOGV("... name = %s", c_name); + LOGV("... uuid1 = %llX", uuidMsb); + LOGV("... uuid2 = %llX", uuidLsb); + LOGV("... channel = %d", channel); + DBusMessage *reply = dbus_func_args(env, nat->conn, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "AddRfcommServiceRecord", + DBUS_TYPE_STRING, &c_name, + DBUS_TYPE_UINT64, &uuidMsb, + DBUS_TYPE_UINT64, &uuidLsb, + DBUS_TYPE_UINT16, &channel, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(name, c_name); + return reply ? dbus_returns_uint32(env, reply) : -1; + } +#endif + return -1; +} + +static jboolean removeServiceRecordNative(JNIEnv *env, jobject object, jint handle) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + LOGV("... handle = %X", handle); + DBusMessage *reply = dbus_func_args(env, nat->conn, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "RemoveServiceRecord", + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID); + return reply ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative}, + {"setupNativeDataNative", "()Z", (void *)setupNativeDataNative}, + {"tearDownNativeDataNative", "()Z", (void *)tearDownNativeDataNative}, + {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, + {"getAdapterPathNative", "()Ljava/lang/String;", (void*)getAdapterPathNative}, + + {"isEnabledNative", "()I", (void *)isEnabledNative}, + {"enableNative", "()I", (void *)enableNative}, + {"disableNative", "()I", (void *)disableNative}, + + {"getAdapterPropertiesNative", "()[Ljava/lang/Object;", (void *)getAdapterPropertiesNative}, + {"getDevicePropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;", + (void *)getDevicePropertiesNative}, + {"setAdapterPropertyStringNative", "(Ljava/lang/String;Ljava/lang/String;)Z", + (void *)setAdapterPropertyStringNative}, + {"setAdapterPropertyBooleanNative", "(Ljava/lang/String;I)Z", + (void *)setAdapterPropertyBooleanNative}, + {"setAdapterPropertyIntegerNative", "(Ljava/lang/String;I)Z", + (void *)setAdapterPropertyIntegerNative}, + + {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative}, + {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative}, + + {"createPairedDeviceNative", "(Ljava/lang/String;I)Z", (void *)createPairedDeviceNative}, + {"cancelDeviceCreationNative", "(Ljava/lang/String;)Z", (void *)cancelDeviceCreationNative}, + {"removeDeviceNative", "(Ljava/lang/String;)Z", (void *)removeDeviceNative}, + {"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I", + (void *)getDeviceServiceChannelNative}, + + {"setPairingConfirmationNative", "(Ljava/lang/String;ZI)Z", + (void *)setPairingConfirmationNative}, + {"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative}, + {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative}, + {"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z", + (void *)cancelPairingUserInputNative}, + {"setDevicePropertyBooleanNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", + (void *)setDevicePropertyBooleanNative}, + {"createDeviceNative", "(Ljava/lang/String;)Z", (void *)createDeviceNative}, + {"discoverServicesNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)discoverServicesNative}, + {"addRfcommServiceRecordNative", "(Ljava/lang/String;JJS)I", (void *)addRfcommServiceRecordNative}, + {"removeServiceRecordNative", "(I)Z", (void *)removeServiceRecordNative}, +}; + +int register_android_server_BluetoothService(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/server/BluetoothService", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp index 98f4e03..fde6ca6 100644 --- a/core/jni/android_text_format_Time.cpp +++ b/core/jni/android_text_format_Time.cpp @@ -367,10 +367,12 @@ static int get_char(JNIEnv* env, const jchar *s, int spos, int mul, if (c >= '0' && c <= '9') { return (c - '0') * mul; } else { - char msg[100]; - sprintf(msg, "Parse error at pos=%d", spos); - jniThrowException(env, "android/util/TimeFormatException", msg); - *thrown = true; + if (!*thrown) { + char msg[100]; + sprintf(msg, "Parse error at pos=%d", spos); + jniThrowException(env, "android/util/TimeFormatException", msg); + *thrown = true; + } return 0; } } diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 7325432..f0885fd 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -25,12 +25,12 @@ #include #include -#include -#include +#include +#include #include -#include -#include -#include +#include +#include +#include #include diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h index 16d993d..495e76a 100644 --- a/core/jni/android_util_Binder.h +++ b/core/jni/android_util_Binder.h @@ -15,7 +15,7 @@ ** limitations under the License. */ -#include +#include #include "jni.h" diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp index 5e5103a..34b7c89 100644 --- a/core/jni/android_util_EventLog.cpp +++ b/core/jni/android_util_EventLog.cpp @@ -51,17 +51,17 @@ struct ByteBuf { size_t len; size_t capacity; uint8_t* buf; - + ByteBuf(size_t initSize) { buf = (uint8_t*)malloc(initSize); len = 0; - capacity = initSize; + capacity = initSize; } - + ~ByteBuf() { free(buf); } - + bool ensureExtraCapacity(size_t extra) { size_t spaceNeeded = len + extra; if (spaceNeeded > capacity) { @@ -77,7 +77,7 @@ struct ByteBuf { return true; } } - + void putIntEvent(jint value) { bool succeeded = ensureExtraCapacity(INT_BUFFER_SIZE); buf[len++] = EVENT_TYPE_INT; @@ -162,7 +162,7 @@ static jint android_util_EventLog_writeEvent_Integer(JNIEnv* env, jobject clazz, * In class android.util.EventLog: * static native int writeEvent(long tag, long value) */ -static jint android_util_EventLog_writeEvent_Long(JNIEnv* env, jobject clazz, +static jint android_util_EventLog_writeEvent_Long(JNIEnv* env, jobject clazz, jint tag, jlong value) { return android_btWriteLog(tag, EVENT_TYPE_LONG, &value, sizeof(value)); @@ -210,6 +210,8 @@ static jint android_util_EventLog_writeEvent_String(JNIEnv* env, jobject clazz, /* * In class android.util.EventLog: * static native void readEvents(int[] tags, Collection output) + * + * Reads events from the event log, typically /dev/log/events */ static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz, jintArray tags, @@ -273,6 +275,80 @@ static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz, env->ReleaseIntArrayElements(tags, tagValues, 0); } +/* + * In class android.util.EventLog: + * static native void readEvents(String path, Collection output) + * + * Reads events from a file (See Checkin.Aggregation). Events are stored in + * native raw format (logger_entry + payload). + */ +static void android_util_EventLog_readEventsFile(JNIEnv* env, jobject clazz, jstring path, + jobject out) { + if (path == NULL || out == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const char *pathString = env->GetStringUTFChars(path, 0); + int fd = open(pathString, O_RDONLY | O_NONBLOCK); + env->ReleaseStringUTFChars(path, pathString); + + if (fd < 0) { + jniThrowIOException(env, errno); + return; + } + + uint8_t buf[LOGGER_ENTRY_MAX_LEN]; + for (;;) { + // read log entry structure from file + int len = read(fd, buf, sizeof(logger_entry)); + if (len == 0) { + break; // end of file + } else if (len < 0) { + jniThrowIOException(env, errno); + } else if ((size_t) len < sizeof(logger_entry)) { + jniThrowException(env, "java/io/IOException", "Event header too short"); + break; + } + + // read event payload + logger_entry* entry = (logger_entry*) buf; + if (entry->len > LOGGER_ENTRY_MAX_PAYLOAD) { + jniThrowException(env, + "java/lang/IllegalArgumentException", + "Too much data for event payload. Corrupt file?"); + break; + } + + len = read(fd, buf + sizeof(logger_entry), entry->len); + if (len == 0) { + break; // end of file + } else if (len < 0) { + jniThrowIOException(env, errno); + } else if ((size_t) len < entry->len) { + jniThrowException(env, "java/io/IOException", "Event payload too short"); + break; + } + + // create EventLog$Event and add it to the collection + int buffer_size = sizeof(logger_entry) + entry->len; + jbyteArray array = env->NewByteArray(buffer_size); + if (array == NULL) break; + + jbyte *bytes = env->GetByteArrayElements(array, NULL); + memcpy(bytes, buf, buffer_size); + env->ReleaseByteArrayElements(array, bytes, 0); + + jobject event = env->NewObject(gEventClass, gEventInitID, array); + if (event == NULL) break; + + env->CallBooleanMethod(out, gCollectionAddID, event); + env->DeleteLocalRef(event); + env->DeleteLocalRef(array); + } + + close(fd); +} /* * JNI registration. @@ -292,6 +368,10 @@ static JNINativeMethod gRegisterMethods[] = { { "readEvents", "([ILjava/util/Collection;)V", (void*) android_util_EventLog_readEvents + }, + { "readEvents", + "(Ljava/lang/String;Ljava/util/Collection;)V", + (void*) android_util_EventLog_readEventsFile } }; diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 98fe0e65..d8c2234 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -18,9 +18,9 @@ #define LOG_TAG "Process" #include -#include -#include -#include +#include +#include +#include #include #include @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -50,13 +51,7 @@ pid_t gettid() { return syscall(__NR_gettid);} #undef __KERNEL__ #endif -/* - * List of cgroup names which map to ANDROID_TGROUP_ values in Thread.h - * and Process.java - * These names are used to construct the path to the cgroup control dir - */ - -static const char *cgroup_names[] = { NULL, "bg_non_interactive", "fg_boost" }; +#define POLICY_DEBUG 0 using namespace android; @@ -194,28 +189,6 @@ jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name) return -1; } -static int add_pid_to_cgroup(int pid, int grp) -{ - int fd; - char path[255]; - char text[64]; - - sprintf(path, "/dev/cpuctl/%s/tasks", - (cgroup_names[grp] ? cgroup_names[grp] : "")); - - if ((fd = open(path, O_WRONLY)) < 0) - return -1; - - sprintf(text, "%d", pid); - if (write(fd, text, strlen(text)) < 0) { - close(fd); - return -1; - } - - close(fd); - return 0; -} - void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint grp) { if (grp > ANDROID_TGROUP_MAX || grp < 0) { @@ -223,10 +196,9 @@ void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint return; } - if (add_pid_to_cgroup(pid, grp)) { - // If the thread exited on us, don't generate an exception - if (errno != ESRCH && errno != ENOENT) - signalExceptionForGroupError(env, clazz, errno); + if (set_sched_policy(pid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ? + SP_BACKGROUND : SP_FOREGROUND)) { + signalExceptionForGroupError(env, clazz, errno); } } @@ -242,6 +214,26 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin return; } +#if POLICY_DEBUG + char cmdline[32]; + int fd; + + strcpy(cmdline, "unknown"); + + sprintf(proc_path, "/proc/%d/cmdline", pid); + fd = open(proc_path, O_RDONLY); + if (fd >= 0) { + int rc = read(fd, cmdline, sizeof(cmdline)-1); + cmdline[rc] = 0; + close(fd); + } + + if (grp == ANDROID_TGROUP_BG_NONINTERACT) { + LOGD("setProcessGroup: vvv pid %d (%s)", pid, cmdline); + } else { + LOGD("setProcessGroup: ^^^ pid %d (%s)", pid, cmdline); + } +#endif sprintf(proc_path, "/proc/%d/task", pid); if (!(d = opendir(proc_path))) { // If the process exited on us, don't generate an exception @@ -271,13 +263,9 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin continue; } - if (add_pid_to_cgroup(t_pid, grp)) { - // If the thread exited on us, ignore it and keep going - if (errno != ESRCH && errno != ENOENT) { - signalExceptionForGroupError(env, clazz, errno); - closedir(d); - return; - } + if (set_sched_policy(t_pid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ? + SP_BACKGROUND : SP_FOREGROUND)) { + signalExceptionForGroupError(env, clazz, errno); } } closedir(d); @@ -286,10 +274,16 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz, jint pid, jint pri) { + int rc = 0; + if (pri >= ANDROID_PRIORITY_BACKGROUND) { - add_pid_to_cgroup(pid, ANDROID_TGROUP_BG_NONINTERACT); + rc = set_sched_policy(pid, SP_BACKGROUND); } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) { - add_pid_to_cgroup(pid, ANDROID_TGROUP_DEFAULT); + rc = set_sched_policy(pid, SP_FOREGROUND); + } + + if (rc) { + signalExceptionForGroupError(env, clazz, errno); } if (setpriority(PRIO_PROCESS, pid, pri) < 0) { @@ -385,7 +379,7 @@ static int pid_compare(const void* v1, const void* v2) return *((const jint*)v1) - *((const jint*)v2); } -jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz) +static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz) { int fd = open("/proc/meminfo", O_RDONLY); @@ -405,7 +399,7 @@ jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz) buffer[len] = 0; int numFound = 0; - int mem = 0; + jlong mem = 0; static const char* const sums[] = { "MemFree:", "Cached:", NULL }; static const int sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), NULL }; @@ -424,7 +418,7 @@ jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz) p++; if (*p == 0) p--; } - mem += atoi(num) * 1024; + mem += atoll(num) * 1024; numFound++; break; } @@ -874,7 +868,7 @@ static const JNINativeMethod methods[] = { {"setGid", "(I)I", (void*)android_os_Process_setGid}, {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, {"supportsProcesses", "()Z", (void*)android_os_Process_supportsProcesses}, - {"getFreeMemory", "()I", (void*)android_os_Process_getFreeMemory}, + {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory}, {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines}, {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids}, {"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_readProcFile}, diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 076775f..40c8aa0 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -24,6 +24,7 @@ #include #include +#include #include "jni.h" #include @@ -45,6 +46,7 @@ struct sso_t { static sso_t sso; struct so_t { + jfieldID surfaceControl; jfieldID surface; jfieldID saveCount; jfieldID canvas; @@ -121,10 +123,50 @@ static void SurfaceSession_kill(JNIEnv* env, jobject clazz) // ---------------------------------------------------------------------------- +static sp getSurfaceControl(JNIEnv* env, jobject clazz) +{ + SurfaceControl* const p = + (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl); + return sp(p); +} + +static void setSurfaceControl(JNIEnv* env, jobject clazz, + const sp& surface) +{ + SurfaceControl* const p = + (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl); + if (surface.get()) { + surface->incStrong(clazz); + } + if (p) { + p->decStrong(clazz); + } + env->SetIntField(clazz, so.surfaceControl, (int)surface.get()); +} + static sp getSurface(JNIEnv* env, jobject clazz) { - Surface* const p = (Surface*)env->GetIntField(clazz, so.surface); - return sp(p); + sp result((Surface*)env->GetIntField(clazz, so.surface)); + if (result == 0) { + /* + * if this method is called from the WindowManager's process, it means + * the client is is not remote, and therefore is allowed to have + * a Surface (data), so we create it here. + * If we don't have a SurfaceControl, it means we're in a different + * process. + */ + + SurfaceControl* const control = + (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl); + if (control) { + result = control->getSurface(); + if (result != 0) { + result->incStrong(clazz); + env->SetIntField(clazz, so.surface, (int)result.get()); + } + } + } + return result; } static void setSurface(JNIEnv* env, jobject clazz, const sp& surface) @@ -153,12 +195,12 @@ static void Surface_init( SurfaceComposerClient* client = (SurfaceComposerClient*)env->GetIntField(session, sso.client); - sp surface(client->createSurface(pid, dpy, w, h, format, flags)); + sp surface(client->createSurface(pid, dpy, w, h, format, flags)); if (surface == 0) { doThrow(env, OutOfResourcesException); return; } - setSurface(env, clazz, surface); + setSurfaceControl(env, clazz, surface); } static void Surface_initParcel(JNIEnv* env, jobject clazz, jobject argParcel) @@ -168,28 +210,44 @@ static void Surface_initParcel(JNIEnv* env, jobject clazz, jobject argParcel) doThrow(env, "java/lang/NullPointerException", NULL); return; } - const sp& rhs = Surface::readFromParcel(parcel); + sp rhs = new Surface(*parcel); setSurface(env, clazz, rhs); } -static void Surface_clear(JNIEnv* env, jobject clazz, uintptr_t *ostack) +static void Surface_destroy(JNIEnv* env, jobject clazz, uintptr_t *ostack) +{ + const sp& surface(getSurfaceControl(env, clazz)); + if (SurfaceControl::isValid(surface)) { + surface->clear(); + } + setSurfaceControl(env, clazz, 0); + setSurface(env, clazz, 0); +} + +static void Surface_release(JNIEnv* env, jobject clazz, uintptr_t *ostack) { + setSurfaceControl(env, clazz, 0); setSurface(env, clazz, 0); } static jboolean Surface_isValid(JNIEnv* env, jobject clazz) { - const sp& surface = getSurface(env, clazz); - return surface->isValid() ? JNI_TRUE : JNI_FALSE; + const sp& surfaceControl(getSurfaceControl(env, clazz)); + if (surfaceControl != 0) { + return SurfaceControl::isValid(surfaceControl) ? JNI_TRUE : JNI_FALSE; + } + const sp& surface(getSurface(env, clazz)); + return Surface::isValid(surface) ? JNI_TRUE : JNI_FALSE; } static inline SkBitmap::Config convertPixelFormat(PixelFormat format) { - /* note: if PIXEL_FORMAT_XRGB_8888 means that all alpha bytes are 0xFF, then + /* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then we can map to SkBitmap::kARGB_8888_Config, and optionally call bitmap.setIsOpaque(true) on the resulting SkBitmap (as an accelerator) */ switch (format) { + case PIXEL_FORMAT_RGBX_8888: return SkBitmap::kARGB_8888_Config; case PIXEL_FORMAT_RGBA_8888: return SkBitmap::kARGB_8888_Config; case PIXEL_FORMAT_RGBA_4444: return SkBitmap::kARGB_4444_Config; case PIXEL_FORMAT_RGB_565: return SkBitmap::kRGB_565_Config; @@ -200,8 +258,8 @@ static inline SkBitmap::Config convertPixelFormat(PixelFormat format) static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect) { - const sp& surface = getSurface(env, clazz); - if (!surface->isValid()) + const sp& surface(getSurface(env, clazz)); + if (!Surface::isValid(surface)) return 0; // get dirty region @@ -212,7 +270,7 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect) dirty.top = env->GetIntField(dirtyRect, ro.t); dirty.right = env->GetIntField(dirtyRect, ro.r); dirty.bottom= env->GetIntField(dirtyRect, ro.b); - if (dirty.left < dirty.right && dirty.top < dirty.bottom) { + if (!dirty.isEmpty()) { dirtyRegion.set(dirty); } } else { @@ -235,7 +293,11 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect) SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas); SkBitmap bitmap; - bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, info.bpr); + ssize_t bpr = info.s * bytesPerPixel(info.format); + bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr); + if (info.format == PIXEL_FORMAT_RGBX_8888) { + bitmap.setIsOpaque(true); + } if (info.w > 0 && info.h > 0) { bitmap.setPixels(info.bits); } else { @@ -243,13 +305,27 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect) bitmap.setPixels(NULL); } nativeCanvas->setBitmapDevice(bitmap); - nativeCanvas->clipRegion(dirtyRegion.toSkRegion()); + + SkRegion clipReg; + if (dirtyRegion.isRect()) { // very common case + const Rect& b(dirtyRegion.getBounds()); + clipReg.setRect(b.left, b.top, b.right, b.bottom); + } else { + size_t count; + Rect const* r = dirtyRegion.getArray(&count); + while (count) { + clipReg.op(r->left, r->top, r->right, r->bottom, SkRegion::kUnion_Op); + r++, count--; + } + } + + nativeCanvas->clipRegion(clipReg); int saveCount = nativeCanvas->save(); env->SetIntField(clazz, so.saveCount, saveCount); if (dirtyRect) { - Rect bounds(dirtyRegion.bounds()); + const Rect& bounds(dirtyRegion.getBounds()); env->SetIntField(dirtyRect, ro.l, bounds.left); env->SetIntField(dirtyRect, ro.t, bounds.top); env->SetIntField(dirtyRect, ro.r, bounds.right); @@ -268,8 +344,8 @@ static void Surface_unlockCanvasAndPost( return; } - const sp& surface = getSurface(env, clazz); - if (!surface->isValid()) + const sp& surface(getSurface(env, clazz)); + if (!Surface::isValid(surface)) return; // detach the canvas from the surface @@ -289,26 +365,8 @@ static void Surface_unlockCanvasAndPost( static void Surface_unlockCanvas( JNIEnv* env, jobject clazz, jobject argCanvas) { - jobject canvas = env->GetObjectField(clazz, so.canvas); - if (canvas != argCanvas) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - return; - } - - const sp& surface = getSurface(env, clazz); - if (!surface->isValid()) - return; - - status_t err = surface->unlock(); - if (err < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - return; - } - SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas); - int saveCount = env->GetIntField(clazz, so.saveCount); - nativeCanvas->restoreToCount(saveCount); - nativeCanvas->setBitmapDevice(SkBitmap()); - env->SetIntField(clazz, so.saveCount, 0); + // XXX: this API has been removed + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_openTransaction( @@ -353,138 +411,140 @@ static void Surface_unfreezeDisplay( static void Surface_setLayer( JNIEnv* env, jobject clazz, jint zorder) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - if (surface->setLayer(zorder) < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - } - } + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + status_t err = surface->setLayer(zorder); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_setPosition( JNIEnv* env, jobject clazz, jint x, jint y) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - if (surface->setPosition(x, y) < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - } - } + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + status_t err = surface->setPosition(x, y); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_setSize( JNIEnv* env, jobject clazz, jint w, jint h) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - if (surface->setSize(w, h) < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - } - } + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + status_t err = surface->setSize(w, h); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_hide( JNIEnv* env, jobject clazz) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - if (surface->hide() < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - } - } + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + status_t err = surface->hide(); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_show( JNIEnv* env, jobject clazz) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - if (surface->show() < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - } - } + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + status_t err = surface->show(); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_freeze( JNIEnv* env, jobject clazz) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - if (surface->freeze() < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - } - } + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + status_t err = surface->freeze(); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_unfreeze( JNIEnv* env, jobject clazz) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - if (surface->unfreeze() < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - } - } + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + status_t err = surface->unfreeze(); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_setFlags( JNIEnv* env, jobject clazz, jint flags, jint mask) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - if (surface->setFlags(flags, mask) < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - } - } + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + status_t err = surface->setFlags(flags, mask); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_setTransparentRegion( JNIEnv* env, jobject clazz, jobject argRegion) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - SkRegion* nativeRegion = (SkRegion*)env->GetIntField(argRegion, no.native_region); - if (surface->setTransparentRegionHint(Region(*nativeRegion)) < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + SkRegion* nativeRegion = (SkRegion*)env->GetIntField(argRegion, no.native_region); + + const SkIRect& b(nativeRegion->getBounds()); + Region reg(Rect(b.fLeft, b.fTop, b.fRight, b.fBottom)); + if (nativeRegion->isComplex()) { + SkRegion::Iterator it(*nativeRegion); + while (!it.done()) { + const SkIRect& r(it.rect()); + reg.addRectUnchecked(r.fLeft, r.fTop, r.fRight, r.fBottom); + it.next(); } } + + status_t err = surface->setTransparentRegionHint(reg); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_setAlpha( JNIEnv* env, jobject clazz, jfloat alpha) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - if (surface->setAlpha(alpha) < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - } - } + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + status_t err = surface->setAlpha(alpha); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_setMatrix( JNIEnv* env, jobject clazz, jfloat dsdx, jfloat dtdx, jfloat dsdy, jfloat dtdy) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - if (surface->setMatrix(dsdx, dtdx, dsdy, dtdy) < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - } - } + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + status_t err = surface->setMatrix(dsdx, dtdx, dsdy, dtdy); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } static void Surface_setFreezeTint( JNIEnv* env, jobject clazz, jint tint) { - const sp& surface = getSurface(env, clazz); - if (surface->isValid()) { - if (surface->setFreezeTint(tint) < 0) { - doThrow(env, "java/lang/IllegalArgumentException", NULL); - } - } + const sp& surface(getSurfaceControl(env, clazz)); + if (surface == 0) return; + status_t err = surface->setFreezeTint(tint); + if (err<0 && err!=NO_INIT) + doThrow(env, "java/lang/IllegalArgumentException", NULL); } +// ---------------------------------------------------------------------------- + static void Surface_copyFrom( JNIEnv* env, jobject clazz, jobject other) { @@ -496,16 +556,21 @@ static void Surface_copyFrom( return; } - const sp& surface = getSurface(env, clazz); - const sp& rhs = getSurface(env, other); - if (!Surface::isSameSurface(surface, rhs)) { + /* + * This is used by the WindowManagerService just after constructing + * a Surface and is necessary for returning the Surface reference to + * the caller. At this point, we should only have a SurfaceControl. + */ + + const sp& surface = getSurfaceControl(env, clazz); + const sp& rhs = getSurfaceControl(env, other); + if (!SurfaceControl::isSameSurface(surface, rhs)) { // we reassign the surface only if it's a different one // otherwise we would loose our client-side state. - setSurface(env, clazz, rhs->dup()); + setSurfaceControl(env, clazz, rhs); } } - static void Surface_readFromParcel( JNIEnv* env, jobject clazz, jobject argParcel) { @@ -515,9 +580,9 @@ static void Surface_readFromParcel( return; } - const sp& surface = getSurface(env, clazz); - const sp& rhs = Surface::readFromParcel(parcel); - if (!Surface::isSameSurface(surface, rhs)) { + const sp& control(getSurface(env, clazz)); + sp rhs = new Surface(*parcel); + if (!Surface::isSameSurface(control, rhs)) { // we reassign the surface only if it's a different one // otherwise we would loose our client-side state. setSurface(env, clazz, rhs); @@ -535,8 +600,8 @@ static void Surface_writeToParcel( return; } - const sp& surface = getSurface(env, clazz); - Surface::writeToParcel(surface, parcel); + const sp& control(getSurfaceControl(env, clazz)); + SurfaceControl::writeSurfaceToParcel(control, parcel); } // ---------------------------------------------------------------------------- @@ -557,7 +622,8 @@ static JNINativeMethod gSurfaceMethods[] = { {"nativeClassInit", "()V", (void*)nativeClassInit }, {"init", "(Landroid/view/SurfaceSession;IIIIII)V", (void*)Surface_init }, {"init", "(Landroid/os/Parcel;)V", (void*)Surface_initParcel }, - {"clear", "()V", (void*)Surface_clear }, + {"destroy", "()V", (void*)Surface_destroy }, + {"release", "()V", (void*)Surface_release }, {"copyFrom", "(Landroid/view/Surface;)V", (void*)Surface_copyFrom }, {"isValid", "()Z", (void*)Surface_isValid }, {"lockCanvasNative", "(Landroid/graphics/Rect;)Landroid/graphics/Canvas;", (void*)Surface_lockCanvas }, @@ -586,7 +652,8 @@ static JNINativeMethod gSurfaceMethods[] = { void nativeClassInit(JNIEnv* env, jclass clazz) { - so.surface = env->GetFieldID(clazz, "mSurface", "I"); + so.surface = env->GetFieldID(clazz, "mSurface", "I"); + so.surfaceControl = env->GetFieldID(clazz, "mSurfaceControl", "I"); so.saveCount = env->GetFieldID(clazz, "mSaveCount", "I"); so.canvas = env->GetFieldID(clazz, "mCanvas", "Landroid/graphics/Canvas;"); diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp index 4452065..974fc0b 100644 --- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -338,7 +337,7 @@ not_valid_surface: goto not_valid_surface; jint* base = beginNativeAttribList(_env, attrib_list); - EGLSurface sur = eglCreateWindowSurface(dpy, cnf, new EGLNativeWindowSurface(window), base); + EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window, base); endNativeAttributeList(_env, attrib_list, base); return (jint)sur; } diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp index 89b1f96..3e0aea5 100644 --- a/core/jni/com_google_android_gles_jni_GLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp @@ -3593,6 +3593,10 @@ android_glBufferData__IILjava_nio_Buffer_2I if (data_buf) { data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining); + if (_remaining < size) { + _env->ThrowNew(IAEClass, "remaining() < size"); + goto exit; + } } glBufferData( (GLenum)target, @@ -3600,6 +3604,8 @@ android_glBufferData__IILjava_nio_Buffer_2I (GLvoid *)data, (GLenum)usage ); + +exit: if (_array) { releasePointer(_env, _array, data, JNI_FALSE); } @@ -3614,12 +3620,18 @@ android_glBufferSubData__IIILjava_nio_Buffer_2 GLvoid *data = (GLvoid *) 0; data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining); + if (_remaining < size) { + _env->ThrowNew(IAEClass, "remaining() < size"); + goto exit; + } glBufferSubData( (GLenum)target, (GLintptr)offset, (GLsizeiptr)size, (GLvoid *)data ); + +exit: if (_array) { releasePointer(_env, _array, data, JNI_FALSE); } -- cgit v1.1