diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
commit | f013e1afd1e68af5e3b868c26a653bbfb39538f8 (patch) | |
tree | 7ad6c8fd9c7b55f4b4017171dec1cb760bbd26bf /core/jni | |
parent | e70cfafe580c6f2994c4827cd8a534aabf3eb05c (diff) | |
download | frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.zip frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.gz frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'core/jni')
33 files changed, 3224 insertions, 1014 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index e9009e6..2c74ab7 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -11,6 +11,10 @@ else LOCAL_CFLAGS += -DPACKED="" endif +ifneq ($(USE_CUSTOM_RUNTIME_HEAP_MAX),) + LOCAL_CFLAGS += -DCUSTOM_RUNTIME_HEAP_MAX=$(USE_CUSTOM_RUNTIME_HEAP_MAX) +endif + LOCAL_SRC_FILES:= \ ActivityManager.cpp \ AndroidRuntime.cpp \ @@ -45,7 +49,7 @@ LOCAL_SRC_FILES:= \ android_net_wifi_Wifi.cpp \ android_nio_utils.cpp \ android_pim_EventRecurrence.cpp \ - android_pim_Time.cpp \ + android_text_format_Time.cpp \ android_security_Md5MessageDigest.cpp \ android_util_AssetManager.cpp \ android_util_Binder.cpp \ @@ -84,7 +88,9 @@ LOCAL_SRC_FILES:= \ android/graphics/Shader.cpp \ android/graphics/Typeface.cpp \ android/graphics/Xfermode.cpp \ + android_media_AudioRecord.cpp \ android_media_AudioSystem.cpp \ + android_media_AudioTrack.cpp \ android_media_ToneGenerator.cpp \ android_hardware_Camera.cpp \ android_hardware_SensorManager.cpp \ @@ -100,6 +106,7 @@ LOCAL_SRC_FILES:= \ android_bluetooth_ScoSocket.cpp \ android_server_BluetoothDeviceService.cpp \ android_server_BluetoothEventLoop.cpp \ + android_server_BluetoothA2dpService.cpp \ android_message_digest_sha1.cpp \ android_ddm_DdmHandleNativeHeap.cpp \ android_location_GpsLocationProvider.cpp \ @@ -109,7 +116,7 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ $(LOCAL_PATH)/android/graphics \ - $(call include-path-for, corecg graphics) \ + $(call include-path-for, bluedroid corecg graphics) \ $(call include-path-for, libhardware)/hardware \ $(LOCAL_PATH)/../../include/ui \ $(LOCAL_PATH)/../../include/utils \ @@ -128,6 +135,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libnetutils \ libui \ + libskiagl \ libsgl \ libcorecg \ libsqlite \ @@ -151,15 +159,15 @@ LOCAL_CFLAGS += -DHAVE_BLUETOOTH LOCAL_SHARED_LIBRARIES += libbluedroid libdbus endif -ifeq ($(TARGET_ARCH),arm) +ifneq ($(TARGET_SIMULATOR),true) LOCAL_SHARED_LIBRARIES += \ libdl endif LOCAL_LDLIBS += -lpthread -ldl -ifeq ($(TARGET_OS),linux) -ifeq ($(TARGET_ARCH),x86) +ifeq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) LOCAL_LDLIBS += -lrt endif endif diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 2a1184f..f85f7d5 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -71,7 +71,9 @@ extern int register_android_hardware_Camera(JNIEnv *env); extern int register_android_hardware_SensorManager(JNIEnv *env); +extern int register_android_media_AudioRecord(JNIEnv *env); extern int register_android_media_AudioSystem(JNIEnv *env); +extern int register_android_media_AudioTrack(JNIEnv *env); extern int register_android_media_ToneGenerator(JNIEnv *env); extern int register_android_message_digest_sha1(JNIEnv *env); @@ -114,7 +116,7 @@ extern int register_android_database_SQLiteStatement(JNIEnv* env); extern int register_android_debug_JNITest(JNIEnv* env); extern int register_android_nio_utils(JNIEnv* env); extern int register_android_pim_EventRecurrence(JNIEnv* env); -extern int register_android_pim_Time(JNIEnv* env); +extern int register_android_text_format_Time(JNIEnv* env); extern int register_android_os_Debug(JNIEnv* env); extern int register_android_os_ParcelFileDescriptor(JNIEnv *env); extern int register_android_os_Power(JNIEnv *env); @@ -142,6 +144,7 @@ extern int register_android_bluetooth_RfcommSocket(JNIEnv *env); extern int register_android_bluetooth_ScoSocket(JNIEnv *env); extern int register_android_server_BluetoothDeviceService(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); extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env); extern int register_android_util_Base64(JNIEnv* env); @@ -471,42 +474,21 @@ static void blockSigpipe() } /* - * Read the persistent locale from file. + * Read the persistent locale. */ static void readLocale(char* language, char* region) { - char path[512]; char propLang[PROPERTY_VALUE_MAX], propRegn[PROPERTY_VALUE_MAX]; - char *dataDir = getenv("ANDROID_DATA"); - bool found = false; - if (dataDir && strlen(dataDir) < 500) { - strcpy(path, dataDir); - } else { - strcpy(path, "/data"); - } - strcat(path, "/locale"); - FILE* localeFile = fopen(path, "r"); - if (localeFile) { - char line[10]; - char *got = fgets(line, 10, localeFile); - /* Locale code is ll_rr */ - if (got != NULL && strlen(line) >= 5) { - strncat(language, line, 2); - strncat(region, line + 3, 2); - found = true; - } - fclose(localeFile); - } - - if (!found) { + property_get("persist.sys.language", propLang, ""); + property_get("persist.sys.country", propRegn, ""); + if (*propLang == 0 && *propRegn == 0) { /* Set to ro properties, default is en_US */ property_get("ro.product.locale.language", propLang, "en"); property_get("ro.product.locale.region", propRegn, "US"); - strncat(language, propLang, 2); - strncat(region, propRegn, 2); } - + strncat(language, propLang, 2); + strncat(region, propRegn, 2); //LOGD("language=%s region=%s\n", language, region); } @@ -518,8 +500,9 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) JavaVMInitArgs initArgs; JavaVMOption opt; char propBuf[PROPERTY_VALUE_MAX]; - char enableAssertBuf[4 + PROPERTY_VALUE_MAX]; char stackTraceFileBuf[PROPERTY_VALUE_MAX]; + char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX]; + char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX]; char* stackTraceFile = NULL; char* slashClassName = NULL; char* cp; @@ -578,6 +561,9 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) strcpy(enableAssertBuf, "-ea:"); property_get("dalvik.vm.enableassertions", enableAssertBuf+4, ""); + strcpy(jniOptsBuf, "-Xjniopts:"); + property_get("dalvik.vm.jniopts", jniOptsBuf+10, ""); + const char* rootDir = getenv("ANDROID_ROOT"); if (rootDir == NULL) { rootDir = "/system"; @@ -609,20 +595,23 @@ 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 mOptions.add(opt); - /* enable Java "assert" statements in all non-system code */ - //options[curOpt++].optionString = "-ea"; - /* - * Enable or disable bytecode verification. We currently force optimization - * to be enabled when the verifier is off; this is a bad idea, but - * useful while we fiddle with the verifier. + * Enable or disable bytecode verification. * - * This should be coordinated with: - * //device/dalvik/libcore/android/src/main/native/dalvik_system_TouchDex.cpp + * We don't optimize classes that haven't been verified, but that only + * matters if we do "just-in-time" DEX optimization. */ if (verifyJava) { opt.optionString = "-Xverify:all"; @@ -632,7 +621,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) } else { opt.optionString = "-Xverify:none"; mOptions.add(opt); - //opt.optionString = "-Xdexopt:all"; opt.optionString = "-Xdexopt:verified"; mOptions.add(opt); } @@ -697,6 +685,12 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) LOGV("Assertions disabled\n"); } + if (jniOptsBuf[10] != '\0') { + LOGI("JNI options: '%s'\n", jniOptsBuf); + opt.optionString = jniOptsBuf; + mOptions.add(opt); + } + if (stackTraceFileBuf[0] != '\0') { static const char* stfOptName = "-Xstacktracefile:"; @@ -1008,7 +1002,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_util_EventLog), REG_JNI(register_android_util_Log), REG_JNI(register_android_util_FloatMath), - REG_JNI(register_android_pim_Time), + REG_JNI(register_android_text_format_Time), REG_JNI(register_android_pim_EventRecurrence), REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), @@ -1076,7 +1070,9 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_os_ZygoteInit), REG_JNI(register_android_hardware_Camera), REG_JNI(register_android_hardware_SensorManager), + REG_JNI(register_android_media_AudioRecord), REG_JNI(register_android_media_AudioSystem), + REG_JNI(register_android_media_AudioTrack), REG_JNI(register_android_media_ToneGenerator), REG_JNI(register_android_opengl_classes), @@ -1087,6 +1083,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_bluetooth_ScoSocket), REG_JNI(register_android_server_BluetoothDeviceService), REG_JNI(register_android_server_BluetoothEventLoop), + REG_JNI(register_android_server_BluetoothA2dpService), REG_JNI(register_android_message_digest_sha1), REG_JNI(register_android_ddm_DdmHandleNativeHeap), REG_JNI(register_android_util_Base64), diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 8628ec0..27a6349 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -1,573 +1,561 @@ -#include "SkBitmap.h" -#include "SkImageDecoder.h" -#include "SkColorPriv.h" -#include "GraphicsJNI.h" -#include "SkDither.h" - -#include "Parcel.h" -#include "android_util_Binder.h" -#include "android_nio_utils.h" -#include "CreateJavaOutputStreamAdaptor.h" - -#include <jni.h> - -#if 0 - #define TRACE_BITMAP(code) code -#else - #define TRACE_BITMAP(code) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Conversions to/from SkColor, for get/setPixels, and the create method, which -// is basically like setPixels - -typedef void (*FromColorProc)(void* dst, const SkColor src[], int width, - int x, int y); - -static void FromColor_D32(void* dst, const SkColor src[], int width, - int, int) { - SkPMColor* d = (SkPMColor*)dst; - - for (int i = 0; i < width; i++) { - *d++ = SkPreMultiplyColor(*src++); - } -} - -static void FromColor_D565(void* dst, const SkColor src[], int width, - int x, int y) { - uint16_t* d = (uint16_t*)dst; - - DITHER_565_SCAN(y); - for (int stop = x + width; x < stop; x++) { - SkColor c = *src++; - *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c), - DITHER_VALUE(x)); - } -} - -static void FromColor_D4444(void* dst, const SkColor src[], int width, - int x, int y) { - SkPMColor16* d = (SkPMColor16*)dst; - - DITHER_4444_SCAN(y); - for (int stop = x + width; x < stop; x++) { - SkPMColor c = SkPreMultiplyColor(*src++); - *d++ = SkDitherARGB32To4444(c, DITHER_VALUE(x)); -// *d++ = SkPixel32ToPixel4444(c); - } -} - -// can return NULL -static FromColorProc ChooseFromColorProc(SkBitmap::Config config) { - switch (config) { - case SkBitmap::kARGB_8888_Config: - return FromColor_D32; - case SkBitmap::kARGB_4444_Config: - return FromColor_D4444; - case SkBitmap::kRGB_565_Config: - return FromColor_D565; - default: - break; - } - return NULL; -} - -bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, - int srcOffset, int srcStride, - int x, int y, int width, int height, - const SkBitmap& dstBitmap) { - SkAutoLockPixels alp(dstBitmap); - void* dst = dstBitmap.getPixels(); - FromColorProc proc = ChooseFromColorProc(dstBitmap.config()); - - if (NULL == dst || NULL == proc) { - return false; - } - - jint* array = env->GetIntArrayElements(srcColors, NULL); - const SkColor* src = (const SkColor*)array + srcOffset; - - // reset to to actual choice from caller - dst = dstBitmap.getAddr(x, y); - // now copy/convert each scanline - for (int y = 0; y < height; y++) { - proc(dst, src, width, x, y); - src += srcStride; - dst = (char*)dst + dstBitmap.rowBytes(); - } - - env->ReleaseIntArrayElements(srcColors, array, 0); - return true; -} - -//////////////////// ToColor procs - -typedef void (*ToColorProc)(SkColor dst[], const void* src, int width, - SkColorTable*); - -static inline SkColor pmcolorToColor(SkPMColor c) { - if (0 == c) { - return 0; - } - - unsigned a = SkGetPackedA32(c); - unsigned r = SkGetPackedR32(c); - unsigned g = SkGetPackedG32(c); - unsigned b = SkGetPackedB32(c); - - if (a < 255) { - SkFixed scale = SK_Fixed1 / a; - r = SkFixedRound(r * scale); - g = SkFixedRound(g * scale); - b = SkFixedRound(b * scale); - SkASSERT(r <= 0xFF); - SkASSERT(g <= 0xFF); - SkASSERT(b <= 0xFF); - } - - return SkColorSetARGB(a, r, g, b); -} - -static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width, - SkColorTable*) { - SkASSERT(width > 0); - const SkPMColor* s = (const SkPMColor*)src; - do { - *dst++ = pmcolorToColor(*s++); - } while (--width != 0); -} - -static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width, - SkColorTable*) { - SkASSERT(width > 0); - const SkPMColor* s = (const SkPMColor*)src; - do { - SkPMColor c = *s++; - *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), - SkGetPackedB32(c)); - } while (--width != 0); -} - -static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width, - SkColorTable*) { - SkASSERT(width > 0); - const SkPMColor16* s = (const SkPMColor16*)src; - do { - *dst++ = pmcolorToColor(SkPixel4444ToPixel32(*s++)); - } while (--width != 0); -} - -static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width, - SkColorTable*) { - SkASSERT(width > 0); - const SkPMColor* s = (const SkPMColor*)src; - do { - SkPMColor c = SkPixel4444ToPixel32(*s++); - *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), - SkGetPackedB32(c)); - } while (--width != 0); -} - -static void ToColor_S565(SkColor dst[], const void* src, int width, - SkColorTable*) { - SkASSERT(width > 0); - const uint16_t* s = (const uint16_t*)src; - do { - uint16_t c = *s++; - *dst++ = SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c), - SkPacked16ToB32(c)); - } while (--width != 0); -} - -static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width, - SkColorTable* ctable) { - SkASSERT(width > 0); - const uint8_t* s = (const uint8_t*)src; - const SkPMColor* colors = ctable->lockColors(); - do { - *dst++ = pmcolorToColor(colors[*s++]); - } while (--width != 0); - ctable->unlockColors(false); -} - -static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width, - SkColorTable* ctable) { - SkASSERT(width > 0); - const uint8_t* s = (const uint8_t*)src; - const SkPMColor* colors = ctable->lockColors(); - do { - SkPMColor c = colors[*s++]; - *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), - SkGetPackedB32(c)); - } while (--width != 0); - ctable->unlockColors(false); -} - -// can return NULL -static ToColorProc ChooseToColorProc(const SkBitmap& src) { - switch (src.config()) { - case SkBitmap::kARGB_8888_Config: - return src.isOpaque() ? ToColor_S32_Opaque : ToColor_S32_Alpha; - case SkBitmap::kARGB_4444_Config: - return src.isOpaque() ? ToColor_S4444_Opaque : ToColor_S4444_Alpha; - case SkBitmap::kRGB_565_Config: - return ToColor_S565; - case SkBitmap::kIndex8_Config: - if (src.getColorTable() == NULL) { - return NULL; - } - return src.isOpaque() ? ToColor_SI8_Opaque : ToColor_SI8_Alpha; - default: - break; - } - return NULL; -} - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// - -static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, - int offset, int stride, int width, int height, - SkBitmap::Config config, jboolean isMutable) { - if (width <= 0 || height <= 0) { - doThrowIAE(env, "width and height must be > 0"); - return NULL; - } - - if (NULL != jColors) { - size_t n = env->GetArrayLength(jColors); - if (n < SkAbs32(stride) * (size_t)height) { - doThrowAIOOBE(env); - return NULL; - } - } - - SkBitmap bitmap; - - bitmap.setConfig(config, width, height); - if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL)) { - return NULL; - } - - if (jColors != NULL) { - GraphicsJNI::SetPixels(env, jColors, offset, stride, - 0, 0, width, height, bitmap); - } - - return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable, - NULL); -} - -static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src, - SkBitmap::Config dstConfig, jboolean isMutable) { - SkBitmap result; - JavaPixelAllocator allocator(env); - - if (!src->copyTo(&result, dstConfig, &allocator)) { - return NULL; - } - - return GraphicsJNI::createBitmap(env, new SkBitmap(result), isMutable, - NULL); -} - -static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) { - delete bitmap; -} - -static void Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) { - bitmap->setPixels(NULL, NULL); -} - -// These must match the int values in Bitmap.java -enum JavaEncodeFormat { - kJPEG_JavaEncodeFormat = 0, - kPNG_JavaEncodeFormat = 1 -}; - -static bool Bitmap_compress(JNIEnv* env, jobject clazz, SkBitmap* bitmap, - int format, int quality, - jobject jstream, jbyteArray jstorage) { - SkImageEncoder::Type fm; - - switch (format) { - case kJPEG_JavaEncodeFormat: - fm = SkImageEncoder::kJPEG_Type; - break; - case kPNG_JavaEncodeFormat: - fm = SkImageEncoder::kPNG_Type; - break; - default: - return false; - } - - bool success = false; - SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); - if (NULL != strm) { - SkImageEncoder* encoder = SkImageEncoder::Create(fm); - if (NULL != encoder) { - success = encoder->encodeStream(strm, *bitmap, quality); - delete encoder; - } - delete strm; - } - return success; -} - -static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) { - bitmap->eraseColor(color); -} - -static int Bitmap_width(JNIEnv* env, jobject, SkBitmap* bitmap) { - return bitmap->width(); -} - -static int Bitmap_height(JNIEnv* env, jobject, SkBitmap* bitmap) { - return bitmap->height(); -} - -static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) { - return bitmap->rowBytes(); -} - -static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) { - return bitmap->config(); -} - -static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) { - return !bitmap->isOpaque(); -} - -/////////////////////////////////////////////////////////////////////////////// - -static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { - if (parcel == NULL) { - SkDebugf("-------- unparcel parcel is NULL\n"); - return NULL; - } - - android::Parcel* p = android::parcelForJavaObject(env, parcel); - - const bool isMutable = p->readInt32() != 0; - const SkBitmap::Config config = (SkBitmap::Config)p->readInt32(); - const int width = p->readInt32(); - const int height = p->readInt32(); - const int rowBytes = p->readInt32(); - - if (SkBitmap::kARGB_8888_Config != config && - SkBitmap::kRGB_565_Config != config && - SkBitmap::kARGB_4444_Config != config && - SkBitmap::kIndex8_Config != config && - SkBitmap::kA8_Config != config) { - SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config); - return NULL; - } - - SkBitmap* bitmap = new SkBitmap; - - bitmap->setConfig(config, width, height, rowBytes); - - SkColorTable* ctable = NULL; - if (config == SkBitmap::kIndex8_Config) { - int count = p->readInt32(); - if (count > 0) { - size_t size = count * sizeof(SkPMColor); - const SkPMColor* src = (const SkPMColor*)p->readInplace(size); - ctable = new SkColorTable(src, count); - } - } - - if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable)) { - ctable->safeUnref(); - delete bitmap; - return NULL; - } - - ctable->safeUnref(); - - size_t size = bitmap->getSize(); - bitmap->lockPixels(); - memcpy(bitmap->getPixels(), p->readInplace(size), size); - bitmap->unlockPixels(); - - return GraphicsJNI::createBitmap(env, bitmap, isMutable, NULL); -} - -static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, - const SkBitmap* bitmap, - jboolean isMutable, jobject parcel) { - if (parcel == NULL) { - SkDebugf("------- writeToParcel null parcel\n"); - return false; - } - - android::Parcel* p = android::parcelForJavaObject(env, parcel); - - p->writeInt32(isMutable); - p->writeInt32(bitmap->config()); - p->writeInt32(bitmap->width()); - p->writeInt32(bitmap->height()); - p->writeInt32(bitmap->rowBytes()); - - if (bitmap->getConfig() == SkBitmap::kIndex8_Config) { - SkColorTable* ctable = bitmap->getColorTable(); - if (ctable != NULL) { - int count = ctable->count(); - p->writeInt32(count); - memcpy(p->writeInplace(count * sizeof(SkPMColor)), - ctable->lockColors(), count * sizeof(SkPMColor)); - ctable->unlockColors(false); - } else { - p->writeInt32(0); // indicate no ctable - } - } - - size_t size = bitmap->getSize(); - bitmap->lockPixels(); - memcpy(p->writeInplace(size), bitmap->getPixels(), size); - bitmap->unlockPixels(); - return true; -} - -static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, - const SkBitmap* src, const SkPaint* paint, - jintArray offsetXY) { - SkIPoint offset; - SkBitmap* dst = new SkBitmap; - - src->extractAlpha(dst, paint, &offset); - if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { - int* array = env->GetIntArrayElements(offsetXY, NULL); - array[0] = offset.fX; - array[1] = offset.fY; - env->ReleaseIntArrayElements(offsetXY, array, 0); - } - - return GraphicsJNI::createBitmap(env, dst, true, NULL); -} - -/////////////////////////////////////////////////////////////////////////////// - -static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap, - int x, int y) { - SkAutoLockPixels alp(*bitmap); - - ToColorProc proc = ChooseToColorProc(*bitmap); - if (NULL == proc) { - return 0; - } - const void* src = bitmap->getAddr(x, y); - if (NULL == src) { - return 0; - } - - SkColor dst[1]; - proc(dst, src, 1, bitmap->getColorTable()); - return dst[0]; -} - -static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap, - jintArray pixelArray, int offset, int stride, - int x, int y, int width, int height) { - SkAutoLockPixels alp(*bitmap); - - ToColorProc proc = ChooseToColorProc(*bitmap); - if (NULL == proc) { - return; - } - const void* src = bitmap->getAddr(x, y); - if (NULL == src) { - return; - } - - SkColorTable* ctable = bitmap->getColorTable(); - jint* dst = env->GetIntArrayElements(pixelArray, NULL); - SkColor* d = (SkColor*)dst + offset; - while (--height >= 0) { - proc(d, src, width, ctable); - d += stride; - src = (void*)((const char*)src + bitmap->rowBytes()); - } - env->ReleaseIntArrayElements(pixelArray, dst, 0); -} - -/////////////////////////////////////////////////////////////////////////////// - -static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap, - int x, int y, SkColor color) { - SkAutoLockPixels alp(*bitmap); - if (NULL == bitmap->getPixels()) { - return; - } - - FromColorProc proc = ChooseFromColorProc(bitmap->config()); - if (NULL == proc) { - return; - } - - proc(bitmap->getAddr(x, y), &color, 1, x, y); -} - -static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap, - jintArray pixelArray, int offset, int stride, - int x, int y, int width, int height) { - GraphicsJNI::SetPixels(env, pixelArray, offset, stride, - x, y, width, height, *bitmap); -} - -static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, - const SkBitmap* bitmap, jobject jbuffer) { - SkAutoLockPixels alp(*bitmap); - const void* src = bitmap->getPixels(); - - if (NULL != src) { - android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); - - // the java side has already checked that buffer is large enough - memcpy(abp.pointer(), src, bitmap->getSize()); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -#include <android_runtime/AndroidRuntime.h> - -static JNINativeMethod gBitmapMethods[] = { - { "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;", - (void*)Bitmap_creator }, - { "nativeCopy", "(IIZ)Landroid/graphics/Bitmap;", - (void*)Bitmap_copy }, - { "nativeDestructor", "(I)V", (void*)Bitmap_destructor }, - { "nativeRecycle", "(I)V", (void*)Bitmap_recycle }, - { "nativeCompress", "(IIILjava/io/OutputStream;[B)Z", - (void*)Bitmap_compress }, - { "nativeErase", "(II)V", (void*)Bitmap_erase }, - { "nativeWidth", "(I)I", (void*)Bitmap_width }, - { "nativeHeight", "(I)I", (void*)Bitmap_height }, - { "nativeRowBytes", "(I)I", (void*)Bitmap_rowBytes }, - { "nativeConfig", "(I)I", (void*)Bitmap_config }, - { "nativeHasAlpha", "(I)Z", (void*)Bitmap_hasAlpha }, - { "nativeCreateFromParcel", - "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", - (void*)Bitmap_createFromParcel }, - { "nativeWriteToParcel", "(IZLandroid/os/Parcel;)Z", - (void*)Bitmap_writeToParcel }, - { "nativeExtractAlpha", "(II[I)Landroid/graphics/Bitmap;", - (void*)Bitmap_extractAlpha }, - { "nativeGetPixel", "(III)I", (void*)Bitmap_getPixel }, - { "nativeGetPixels", "(I[IIIIIII)V", (void*)Bitmap_getPixels }, - { "nativeSetPixel", "(IIII)V", (void*)Bitmap_setPixel }, - { "nativeSetPixels", "(I[IIIIIII)V", (void*)Bitmap_setPixels }, - { "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V", - (void*)Bitmap_copyPixelsToBuffer } -}; - -#define kClassPathName "android/graphics/Bitmap" - -int register_android_graphics_Bitmap(JNIEnv* env); -int register_android_graphics_Bitmap(JNIEnv* env) -{ - return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, - gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods)); -} - +#include "SkBitmap.h"
+#include "SkImageDecoder.h"
+#include "SkColorPriv.h"
+#include "GraphicsJNI.h"
+#include "SkDither.h"
+#include "SkUnPreMultiply.h"
+
+#include "Parcel.h"
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <jni.h>
+
+#if 0
+ #define TRACE_BITMAP(code) code
+#else
+ #define TRACE_BITMAP(code)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Conversions to/from SkColor, for get/setPixels, and the create method, which
+// is basically like setPixels
+
+typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
+ int x, int y);
+
+static void FromColor_D32(void* dst, const SkColor src[], int width,
+ int, int) {
+ SkPMColor* d = (SkPMColor*)dst;
+
+ for (int i = 0; i < width; i++) {
+ *d++ = SkPreMultiplyColor(*src++);
+ }
+}
+
+static void FromColor_D565(void* dst, const SkColor src[], int width,
+ int x, int y) {
+ uint16_t* d = (uint16_t*)dst;
+
+ DITHER_565_SCAN(y);
+ for (int stop = x + width; x < stop; x++) {
+ SkColor c = *src++;
+ *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),
+ DITHER_VALUE(x));
+ }
+}
+
+static void FromColor_D4444(void* dst, const SkColor src[], int width,
+ int x, int y) {
+ SkPMColor16* d = (SkPMColor16*)dst;
+
+ DITHER_4444_SCAN(y);
+ for (int stop = x + width; x < stop; x++) {
+ SkPMColor c = SkPreMultiplyColor(*src++);
+ *d++ = SkDitherARGB32To4444(c, DITHER_VALUE(x));
+// *d++ = SkPixel32ToPixel4444(c);
+ }
+}
+
+// can return NULL
+static FromColorProc ChooseFromColorProc(SkBitmap::Config config) {
+ switch (config) {
+ case SkBitmap::kARGB_8888_Config:
+ return FromColor_D32;
+ case SkBitmap::kARGB_4444_Config:
+ return FromColor_D4444;
+ case SkBitmap::kRGB_565_Config:
+ return FromColor_D565;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors,
+ int srcOffset, int srcStride,
+ int x, int y, int width, int height,
+ const SkBitmap& dstBitmap) {
+ SkAutoLockPixels alp(dstBitmap);
+ void* dst = dstBitmap.getPixels();
+ FromColorProc proc = ChooseFromColorProc(dstBitmap.config());
+
+ if (NULL == dst || NULL == proc) {
+ return false;
+ }
+
+ const jint* array = env->GetIntArrayElements(srcColors, NULL);
+ const SkColor* src = (const SkColor*)array + srcOffset;
+
+ // reset to to actual choice from caller
+ dst = dstBitmap.getAddr(x, y);
+ // now copy/convert each scanline
+ for (int y = 0; y < height; y++) {
+ proc(dst, src, width, x, y);
+ src += srcStride;
+ dst = (char*)dst + dstBitmap.rowBytes();
+ }
+
+ env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
+ JNI_ABORT);
+ return true;
+}
+
+//////////////////// ToColor procs
+
+typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
+ SkColorTable*);
+
+static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const SkPMColor* s = (const SkPMColor*)src;
+ do {
+ *dst++ = SkUnPreMultiply::PMColorToColor(*s++);
+ } while (--width != 0);
+}
+
+static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const SkPMColor* s = (const SkPMColor*)src;
+ do {
+ SkPMColor c = *s++;
+ *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+ SkGetPackedB32(c));
+ } while (--width != 0);
+}
+
+static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const SkPMColor16* s = (const SkPMColor16*)src;
+ do {
+ *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));
+ } while (--width != 0);
+}
+
+static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const SkPMColor* s = (const SkPMColor*)src;
+ do {
+ SkPMColor c = SkPixel4444ToPixel32(*s++);
+ *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+ SkGetPackedB32(c));
+ } while (--width != 0);
+}
+
+static void ToColor_S565(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const uint16_t* s = (const uint16_t*)src;
+ do {
+ uint16_t c = *s++;
+ *dst++ = SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),
+ SkPacked16ToB32(c));
+ } while (--width != 0);
+}
+
+static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,
+ SkColorTable* ctable) {
+ SkASSERT(width > 0);
+ const uint8_t* s = (const uint8_t*)src;
+ const SkPMColor* colors = ctable->lockColors();
+ do {
+ *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);
+ } while (--width != 0);
+ ctable->unlockColors(false);
+}
+
+static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
+ SkColorTable* ctable) {
+ SkASSERT(width > 0);
+ const uint8_t* s = (const uint8_t*)src;
+ const SkPMColor* colors = ctable->lockColors();
+ do {
+ SkPMColor c = colors[*s++];
+ *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+ SkGetPackedB32(c));
+ } while (--width != 0);
+ ctable->unlockColors(false);
+}
+
+// can return NULL
+static ToColorProc ChooseToColorProc(const SkBitmap& src) {
+ switch (src.config()) {
+ case SkBitmap::kARGB_8888_Config:
+ return src.isOpaque() ? ToColor_S32_Opaque : ToColor_S32_Alpha;
+ case SkBitmap::kARGB_4444_Config:
+ return src.isOpaque() ? ToColor_S4444_Opaque : ToColor_S4444_Alpha;
+ case SkBitmap::kRGB_565_Config:
+ return ToColor_S565;
+ case SkBitmap::kIndex8_Config:
+ if (src.getColorTable() == NULL) {
+ return NULL;
+ }
+ return src.isOpaque() ? ToColor_SI8_Opaque : ToColor_SI8_Alpha;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
+ int offset, int stride, int width, int height,
+ SkBitmap::Config config, jboolean isMutable) {
+ if (width <= 0 || height <= 0) {
+ doThrowIAE(env, "width and height must be > 0");
+ return NULL;
+ }
+
+ if (NULL != jColors) {
+ size_t n = env->GetArrayLength(jColors);
+ if (n < SkAbs32(stride) * (size_t)height) {
+ doThrowAIOOBE(env);
+ return NULL;
+ }
+ }
+
+ SkBitmap bitmap;
+
+ bitmap.setConfig(config, width, height);
+ if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL)) {
+ return NULL;
+ }
+
+ if (jColors != NULL) {
+ GraphicsJNI::SetPixels(env, jColors, offset, stride,
+ 0, 0, width, height, bitmap);
+ }
+
+ return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable,
+ NULL);
+}
+
+static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
+ SkBitmap::Config dstConfig, jboolean isMutable) {
+ SkBitmap result;
+ JavaPixelAllocator allocator(env);
+
+ if (!src->copyTo(&result, dstConfig, &allocator)) {
+ return NULL;
+ }
+
+ return GraphicsJNI::createBitmap(env, new SkBitmap(result), isMutable,
+ NULL);
+}
+
+static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {
+ delete bitmap;
+}
+
+static void Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) {
+ bitmap->setPixels(NULL, NULL);
+}
+
+// These must match the int values in Bitmap.java
+enum JavaEncodeFormat {
+ kJPEG_JavaEncodeFormat = 0,
+ kPNG_JavaEncodeFormat = 1
+};
+
+static bool Bitmap_compress(JNIEnv* env, jobject clazz, SkBitmap* bitmap,
+ int format, int quality,
+ jobject jstream, jbyteArray jstorage) {
+ SkImageEncoder::Type fm;
+
+ switch (format) {
+ case kJPEG_JavaEncodeFormat:
+ fm = SkImageEncoder::kJPEG_Type;
+ break;
+ case kPNG_JavaEncodeFormat:
+ fm = SkImageEncoder::kPNG_Type;
+ break;
+ default:
+ return false;
+ }
+
+ bool success = false;
+ SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
+ if (NULL != strm) {
+ SkImageEncoder* encoder = SkImageEncoder::Create(fm);
+ if (NULL != encoder) {
+ success = encoder->encodeStream(strm, *bitmap, quality);
+ delete encoder;
+ }
+ delete strm;
+ }
+ return success;
+}
+
+static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) {
+ bitmap->eraseColor(color);
+}
+
+static int Bitmap_width(JNIEnv* env, jobject, SkBitmap* bitmap) {
+ return bitmap->width();
+}
+
+static int Bitmap_height(JNIEnv* env, jobject, SkBitmap* bitmap) {
+ return bitmap->height();
+}
+
+static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) {
+ return bitmap->rowBytes();
+}
+
+static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) {
+ return bitmap->config();
+}
+
+static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) {
+ return !bitmap->isOpaque();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
+ if (parcel == NULL) {
+ SkDebugf("-------- unparcel parcel is NULL\n");
+ return NULL;
+ }
+
+ android::Parcel* p = android::parcelForJavaObject(env, parcel);
+
+ const bool isMutable = p->readInt32() != 0;
+ const SkBitmap::Config config = (SkBitmap::Config)p->readInt32();
+ const int width = p->readInt32();
+ const int height = p->readInt32();
+ const int rowBytes = p->readInt32();
+
+ if (SkBitmap::kARGB_8888_Config != config &&
+ SkBitmap::kRGB_565_Config != config &&
+ SkBitmap::kARGB_4444_Config != config &&
+ SkBitmap::kIndex8_Config != config &&
+ SkBitmap::kA8_Config != config) {
+ SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
+ return NULL;
+ }
+
+ SkBitmap* bitmap = new SkBitmap;
+
+ bitmap->setConfig(config, width, height, rowBytes);
+
+ SkColorTable* ctable = NULL;
+ if (config == SkBitmap::kIndex8_Config) {
+ int count = p->readInt32();
+ if (count > 0) {
+ size_t size = count * sizeof(SkPMColor);
+ const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
+ ctable = new SkColorTable(src, count);
+ }
+ }
+
+ if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable)) {
+ ctable->safeUnref();
+ delete bitmap;
+ return NULL;
+ }
+
+ ctable->safeUnref();
+
+ size_t size = bitmap->getSize();
+ bitmap->lockPixels();
+ memcpy(bitmap->getPixels(), p->readInplace(size), size);
+ bitmap->unlockPixels();
+
+ return GraphicsJNI::createBitmap(env, bitmap, isMutable, NULL);
+}
+
+static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
+ const SkBitmap* bitmap,
+ jboolean isMutable, jobject parcel) {
+ if (parcel == NULL) {
+ SkDebugf("------- writeToParcel null parcel\n");
+ return false;
+ }
+
+ android::Parcel* p = android::parcelForJavaObject(env, parcel);
+
+ p->writeInt32(isMutable);
+ p->writeInt32(bitmap->config());
+ p->writeInt32(bitmap->width());
+ p->writeInt32(bitmap->height());
+ p->writeInt32(bitmap->rowBytes());
+
+ if (bitmap->getConfig() == SkBitmap::kIndex8_Config) {
+ SkColorTable* ctable = bitmap->getColorTable();
+ if (ctable != NULL) {
+ int count = ctable->count();
+ p->writeInt32(count);
+ memcpy(p->writeInplace(count * sizeof(SkPMColor)),
+ ctable->lockColors(), count * sizeof(SkPMColor));
+ ctable->unlockColors(false);
+ } else {
+ p->writeInt32(0); // indicate no ctable
+ }
+ }
+
+ size_t size = bitmap->getSize();
+ bitmap->lockPixels();
+ memcpy(p->writeInplace(size), bitmap->getPixels(), size);
+ bitmap->unlockPixels();
+ return true;
+}
+
+static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
+ const SkBitmap* src, const SkPaint* paint,
+ jintArray offsetXY) {
+ SkIPoint offset;
+ SkBitmap* dst = new SkBitmap;
+
+ src->extractAlpha(dst, paint, &offset);
+ if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
+ int* array = env->GetIntArrayElements(offsetXY, NULL);
+ array[0] = offset.fX;
+ array[1] = offset.fY;
+ env->ReleaseIntArrayElements(offsetXY, array, 0);
+ }
+
+ return GraphicsJNI::createBitmap(env, dst, true, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
+ int x, int y) {
+ SkAutoLockPixels alp(*bitmap);
+
+ ToColorProc proc = ChooseToColorProc(*bitmap);
+ if (NULL == proc) {
+ return 0;
+ }
+ const void* src = bitmap->getAddr(x, y);
+ if (NULL == src) {
+ return 0;
+ }
+
+ SkColor dst[1];
+ proc(dst, src, 1, bitmap->getColorTable());
+ return dst[0];
+}
+
+static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
+ jintArray pixelArray, int offset, int stride,
+ int x, int y, int width, int height) {
+ SkAutoLockPixels alp(*bitmap);
+
+ ToColorProc proc = ChooseToColorProc(*bitmap);
+ if (NULL == proc) {
+ return;
+ }
+ const void* src = bitmap->getAddr(x, y);
+ if (NULL == src) {
+ return;
+ }
+
+ SkColorTable* ctable = bitmap->getColorTable();
+ jint* dst = env->GetIntArrayElements(pixelArray, NULL);
+ SkColor* d = (SkColor*)dst + offset;
+ while (--height >= 0) {
+ proc(d, src, width, ctable);
+ d += stride;
+ src = (void*)((const char*)src + bitmap->rowBytes());
+ }
+ env->ReleaseIntArrayElements(pixelArray, dst, 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
+ int x, int y, SkColor color) {
+ SkAutoLockPixels alp(*bitmap);
+ if (NULL == bitmap->getPixels()) {
+ return;
+ }
+
+ FromColorProc proc = ChooseFromColorProc(bitmap->config());
+ if (NULL == proc) {
+ return;
+ }
+
+ proc(bitmap->getAddr(x, y), &color, 1, x, y);
+}
+
+static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
+ jintArray pixelArray, int offset, int stride,
+ int x, int y, int width, int height) {
+ GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
+ x, y, width, height, *bitmap);
+}
+
+static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
+ const SkBitmap* bitmap, jobject jbuffer) {
+ SkAutoLockPixels alp(*bitmap);
+ const void* src = bitmap->getPixels();
+
+ if (NULL != src) {
+ android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
+
+ // the java side has already checked that buffer is large enough
+ memcpy(abp.pointer(), src, bitmap->getSize());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gBitmapMethods[] = {
+ { "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_creator },
+ { "nativeCopy", "(IIZ)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_copy },
+ { "nativeDestructor", "(I)V", (void*)Bitmap_destructor },
+ { "nativeRecycle", "(I)V", (void*)Bitmap_recycle },
+ { "nativeCompress", "(IIILjava/io/OutputStream;[B)Z",
+ (void*)Bitmap_compress },
+ { "nativeErase", "(II)V", (void*)Bitmap_erase },
+ { "nativeWidth", "(I)I", (void*)Bitmap_width },
+ { "nativeHeight", "(I)I", (void*)Bitmap_height },
+ { "nativeRowBytes", "(I)I", (void*)Bitmap_rowBytes },
+ { "nativeConfig", "(I)I", (void*)Bitmap_config },
+ { "nativeHasAlpha", "(I)Z", (void*)Bitmap_hasAlpha },
+ { "nativeCreateFromParcel",
+ "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_createFromParcel },
+ { "nativeWriteToParcel", "(IZLandroid/os/Parcel;)Z",
+ (void*)Bitmap_writeToParcel },
+ { "nativeExtractAlpha", "(II[I)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_extractAlpha },
+ { "nativeGetPixel", "(III)I", (void*)Bitmap_getPixel },
+ { "nativeGetPixels", "(I[IIIIIII)V", (void*)Bitmap_getPixels },
+ { "nativeSetPixel", "(IIII)V", (void*)Bitmap_setPixel },
+ { "nativeSetPixels", "(I[IIIIIII)V", (void*)Bitmap_setPixels },
+ { "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V",
+ (void*)Bitmap_copyPixelsToBuffer }
+};
+
+#define kClassPathName "android/graphics/Bitmap"
+
+int register_android_graphics_Bitmap(JNIEnv* env);
+int register_android_graphics_Bitmap(JNIEnv* env)
+{
+#if 1
+ return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+ gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
+#else
+ short n = 0;
+ int limit = (char*)env - (char*)0;
+ for (int i = 0; i < limit; i++) {
+ n += i*i;
+ }
+ return n;
+#endif
+}
+
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 90822a1..be8526d 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -43,57 +43,74 @@ public: private: AutoDecoderCancel* fNext; + AutoDecoderCancel* fPrev; jobject fJOptions; // java options object SkImageDecoder* fDecoder; + +#ifdef SK_DEBUG + static void Validate(); +#else + static void Validate() {} +#endif }; static SkMutex gAutoDecoderCancelMutex; static AutoDecoderCancel* gAutoDecoderCancel; +#ifdef SK_DEBUG + static int gAutoDecoderCancelCount; +#endif AutoDecoderCancel::AutoDecoderCancel(jobject joptions, SkImageDecoder* decoder) { fJOptions = joptions; fDecoder = decoder; - // only need to be in the list if we have options if (NULL != joptions) { SkAutoMutexAcquire ac(gAutoDecoderCancelMutex); - + + // Add us as the head of the list + fPrev = NULL; fNext = gAutoDecoderCancel; + if (gAutoDecoderCancel) { + gAutoDecoderCancel->fPrev = this; + } gAutoDecoderCancel = this; + + SkDEBUGCODE(gAutoDecoderCancelCount += 1;) + Validate(); } } AutoDecoderCancel::~AutoDecoderCancel() { - const jobject joptions = fJOptions; - - if (NULL != joptions) { + if (NULL != fJOptions) { SkAutoMutexAcquire ac(gAutoDecoderCancelMutex); - // remove us - AutoDecoderCancel* pair = gAutoDecoderCancel; - AutoDecoderCancel* prev = NULL; - while (pair != NULL) { - AutoDecoderCancel* next = pair->fNext; - if (pair->fJOptions == joptions) { - SkASSERT(pair->fDecoder == fDecoder); - if (prev) { - prev->fNext = next; - } else { - gAutoDecoderCancel = next; - } - return; - } - pair = next; + // take us out of the dllist + AutoDecoderCancel* prev = fPrev; + AutoDecoderCancel* next = fNext; + + if (prev) { + SkASSERT(prev->fNext == this); + prev->fNext = next; + } else { + SkASSERT(gAutoDecoderCancel == this); + gAutoDecoderCancel = next; } - SkDebugf("xxxxxxxxxxxxxxxxxxxxxxx not found in pair list %p %p\n", - fJOptions, fDecoder); + if (next) { + SkASSERT(next->fPrev == this); + next->fPrev = prev; + } + + SkDEBUGCODE(gAutoDecoderCancelCount -= 1;) + Validate(); } } bool AutoDecoderCancel::RequestCancel(jobject joptions) { SkAutoMutexAcquire ac(gAutoDecoderCancelMutex); + Validate(); + AutoDecoderCancel* pair = gAutoDecoderCancel; while (pair != NULL) { if (pair->fJOptions == joptions) { @@ -105,6 +122,37 @@ bool AutoDecoderCancel::RequestCancel(jobject joptions) { return false; } +#ifdef SK_DEBUG +// can only call this inside a lock on gAutoDecoderCancelMutex +void AutoDecoderCancel::Validate() { + const int gCount = gAutoDecoderCancelCount; + + if (gCount == 0) { + SkASSERT(gAutoDecoderCancel == NULL); + } else { + SkASSERT(gCount > 0); + + AutoDecoderCancel* curr = gAutoDecoderCancel; + SkASSERT(curr); + SkASSERT(curr->fPrev == NULL); + + int count = 0; + while (curr) { + count += 1; + SkASSERT(count <= gCount); + if (curr->fPrev) { + SkASSERT(curr->fPrev->fNext == curr); + } + if (curr->fNext) { + SkASSERT(curr->fNext->fPrev == curr); + } + curr = curr->fNext; + } + SkASSERT(count == gCount); + } +} +#endif + /////////////////////////////////////////////////////////////////////////////// using namespace android; @@ -132,6 +180,7 @@ public: // You have to copy the data because it is owned by the png reader Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize); memcpy(patchNew, patch, patchSize); + // this relies on deserialization being done in place Res_png_9patch::deserialize(patchNew); patchNew->fileToDevice(); if (fPatchIsValid) { diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp index 002fdf9..65c2326 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp @@ -28,66 +28,39 @@ public: if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); - printf("------- reset threw an exception\n"); + SkDebugf("------- reset threw an exception\n"); return false; } return true; } - virtual size_t read(void* buffer, size_t size) { + size_t doRead(void* buffer, size_t size) { JNIEnv* env = fEnv; - - if (buffer == NULL && size == 0) { - jint avail = env->CallIntMethod(fJavaInputStream, - gInputStream_availableMethodID); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - printf("------- available threw an exception\n"); - avail = 0; - } - return avail; - } - size_t bytesRead = 0; - - if (buffer == NULL) { // skip - jlong skipped = env->CallLongMethod(fJavaInputStream, - gInputStream_skipMethodID, (jlong)size); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - printf("------- available threw an exception\n"); - return 0; - } - if (skipped < 0) { - return 0; - } - return (size_t)skipped; - } - // read the bytes do { size_t requested = size; if (requested > fCapacity) requested = fCapacity; - + jint n = env->CallIntMethod(fJavaInputStream, - gInputStream_readMethodID, fJavaByteArray, 0, requested); + gInputStream_readMethodID, fJavaByteArray, 0, requested); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); - printf("---- read threw an exception\n"); + SkDebugf("---- read threw an exception\n"); return 0; } - + if (n <= 0) { break; // eof } - - jbyte* array = env->GetByteArrayElements(fJavaByteArray, NULL); + + const jbyte* array = env->GetByteArrayElements(fJavaByteArray, + NULL); memcpy(buffer, array, n); - env->ReleaseByteArrayElements(fJavaByteArray, array, 0); + env->ReleaseByteArrayElements(fJavaByteArray, + const_cast<jbyte*>(array), JNI_ABORT); buffer = (void*)((char*)buffer + n); bytesRead += n; @@ -98,6 +71,64 @@ public: return bytesRead; } + size_t doSkip(size_t size) { + JNIEnv* env = fEnv; + jlong skipped = env->CallLongMethod(fJavaInputStream, + gInputStream_skipMethodID, (jlong)size); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + SkDebugf("------- available threw an exception\n"); + return 0; + } + if (skipped < 0) { + skipped = 0; + } + return (size_t)skipped; + } + + size_t doSize() { + JNIEnv* env = fEnv; + jint avail = env->CallIntMethod(fJavaInputStream, + gInputStream_availableMethodID); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + SkDebugf("------- available threw an exception\n"); + avail = 0; + } + return avail; + } + + virtual size_t read(void* buffer, size_t size) { + JNIEnv* env = fEnv; + if (NULL == buffer) { + if (0 == size) { + return this->doSize(); + } else { + /* InputStream.skip(n) can return <=0 but still not be at EOF + If we see that value, we need to call read(), which will + block if waiting for more data, or return -1 at EOF + */ + size_t amountSkipped = 0; + do { + size_t amount = this->doSkip(size); + if (0 == amount) { + char tmp; + amount = this->doRead(&tmp, 1); + if (0 == amount) { + // if read returned 0, we're at EOF + break; + } + } + amountSkipped += amount; + } while (amountSkipped < size); + return amountSkipped; + } + } + return this->doRead(buffer, size); + } + private: JNIEnv* fEnv; jobject fJavaInputStream; // the caller owns this object @@ -167,7 +198,7 @@ public: if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); - printf("------- write threw an exception\n"); + SkDebugf("------- write threw an exception\n"); return false; } diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index f8ccf72..44113e5 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -265,8 +265,8 @@ void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj) { SkASSERT(env->IsInstanceOf(obj, gPoint_class)); - env->SetIntField(obj, gPointF_xFieldID, ir.fX); - env->SetIntField(obj, gPointF_yFieldID, ir.fY); + env->SetIntField(obj, gPoint_xFieldID, ir.fX); + env->SetIntField(obj, gPoint_yFieldID, ir.fY); } SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point) diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp index 5c48077..de18f9f 100644 --- a/core/jni/android/graphics/Movie.cpp +++ b/core/jni/android/graphics/Movie.cpp @@ -106,7 +106,7 @@ static jobject movie_decodeByteArray(JNIEnv* env, jobject clazz, int totalLength = env->GetArrayLength(byteArray); if ((offset | length) < 0 || offset + length > totalLength) { - doThrow(env, "java/lang/ArrayIndexException"); + doThrow(env, "java/lang/ArrayIndexOutOfBoundsException"); return 0; } diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index a098d89..9e943f3 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -21,13 +21,14 @@ public: if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) { return false; } - jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(obj, 0); - if (array != NULL) - { - Res_png_9patch* chunk = (Res_png_9patch*)array; - int8_t numXDivs = chunk->numXDivs; - env->ReleasePrimitiveArrayCritical(obj, array, 0); - return array[0] != -1; + const jbyte* array = env->GetByteArrayElements(obj, 0); + if (array != NULL) { + const Res_png_9patch* chunk = + reinterpret_cast<const Res_png_9patch*>(array); + int8_t wasDeserialized = chunk->wasDeserialized; + env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array), + JNI_ABORT); + return wasDeserialized != -1; } return false; } @@ -46,17 +47,19 @@ public: static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds, const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint) { - jbyte* array = env->GetByteArrayElements(chunkObj, 0); - if (array != NULL) - { + const jbyte* array = env->GetByteArrayElements(chunkObj, 0); + if (array != NULL) { size_t chunkSize = env->GetArrayLength(chunkObj); - void* deserializedArray = alloca(chunkSize); - Res_png_9patch* chunk = (Res_png_9patch*) deserializedArray; - assert(chunkSize == ((Res_png_9patch*) array)->serializedSize()); + // need to deserialize the chunk + void* storage = alloca(chunkSize); + Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage); memcpy(chunk, array, chunkSize); + assert(chunkSize == chunk->serializedSize()); + // this relies on deserialization being done in place Res_png_9patch::deserialize(chunk); NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); - env->ReleaseByteArrayElements(chunkObj, array, 0); + env->ReleaseByteArrayElements(chunkObj, const_cast<jbyte*>(array), + JNI_ABORT); } } @@ -99,18 +102,20 @@ public: SkRect bounds; GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); - jbyte* array = (jbyte*)env->GetByteArrayElements(chunkObj, 0); - if (array != NULL) - { + const jbyte* array = (jbyte*)env->GetByteArrayElements(chunkObj, 0); + if (array != NULL) { size_t chunkSize = env->GetArrayLength(chunkObj); - void* deserializedArray = alloca(chunkSize); - Res_png_9patch* chunk = (Res_png_9patch*) deserializedArray; - assert(chunkSize == ((Res_png_9patch*) array)->serializedSize()); + // need to deserialize the chunk + void* storage = alloca(chunkSize); + Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage); memcpy(chunk, array, chunkSize); + assert(chunkSize == chunk->serializedSize()); + // this relies on deserialization being done in place Res_png_9patch::deserialize(chunk); SkRegion* region = NULL; NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, ®ion); - env->ReleaseByteArrayElements(chunkObj, array, 0); + env->ReleaseByteArrayElements(chunkObj, const_cast<jbyte*>(array), + JNI_ABORT); return (jint)region; } diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp index ba63ae4..f82053c 100644 --- a/core/jni/android/graphics/NinePatchImpl.cpp +++ b/core/jni/android/graphics/NinePatchImpl.cpp @@ -103,6 +103,10 @@ SkScalar calculateStretch(SkScalar boundsLimit, SkScalar startingPoint, void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap, const android::Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion) { + if (canvas && canvas->quickReject(bounds, SkCanvas::kBW_EdgeType)) { + return; + } + // if our canvas is GL, draw this as a mesh, which will be faster than // in parts (which is faster for raster) if (canvas && canvas->getViewport(NULL)) { diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 05830de..76e6f02 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -313,18 +313,21 @@ public: NPE_CHECK_RETURN_ZERO(env, jpaint); NPE_CHECK_RETURN_ZERO(env, text); - SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); - jchar* textArray = env->GetCharArrayElements(text, NULL); size_t textLength = env->GetArrayLength(text); - + if ((index | count) < 0 || (size_t)(index + count) > textLength) { - doThrow(env, "ArrayIndexOutOfBoundsException"); + doThrow(env, "java/lang/ArrayIndexOutOfBoundsException"); return 0; } - jfloat width = SkScalarToFloat(paint->measureText(textArray + index, count << 1)); - env->ReleaseCharArrayElements(text, textArray, 0); - return width; + const SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); + const jchar* textArray = env->GetCharArrayElements(text, NULL); + // we double count, since measureText wants a byteLength + SkScalar width = paint->measureText(textArray + index, count << 1); + env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), + JNI_ABORT); + + return SkScalarToFloat(width); } static jfloat measureText_StringII(JNIEnv* env, jobject jpaint, jstring text, int start, int end) { @@ -337,7 +340,7 @@ public: int count = end - start; if ((start | count) < 0 || (size_t)count > textLength) { - doThrow(env, "IndexOutOfBoundsException"); + doThrow(env, "java/lang/IndexOutOfBoundsException"); return 0; } @@ -372,9 +375,10 @@ public: } static int getTextWidths___CII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, int index, int count, jfloatArray widths) { - jchar* textArray = env->GetCharArrayElements(text, NULL); + const jchar* textArray = env->GetCharArrayElements(text, NULL); count = dotextwidths(env, paint, textArray + index, count, widths); - env->ReleaseCharArrayElements(text, textArray, 0); + env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), + JNI_ABORT); return count; } @@ -386,9 +390,10 @@ public: } static void getTextPath___CIIFFPath(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, int index, int count, jfloat x, jfloat y, SkPath* path) { - jchar* textArray = env->GetCharArrayElements(text, NULL); + const jchar* textArray = env->GetCharArrayElements(text, NULL); paint->getTextPath(textArray + index, count << 1, SkFloatToScalar(x), SkFloatToScalar(y), path); - env->ReleaseCharArrayElements(text, textArray, 0); + env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), + JNI_ABORT); } static void getTextPath__StringIIFFPath(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, int start, int end, jfloat x, jfloat y, SkPath* path) { @@ -451,10 +456,11 @@ public: } SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); - jchar* text = env->GetCharArrayElements(jtext, NULL); + const jchar* text = env->GetCharArrayElements(jtext, NULL); count = breakText(env, *paint, text + index, count, maxWidth, jmeasuredWidth, tbd); - env->ReleaseCharArrayElements(jtext, text, 0); + env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text), + JNI_ABORT); return count; } @@ -498,9 +504,10 @@ public: static void getCharArrayBounds(JNIEnv* env, jobject, const SkPaint* paint, jcharArray text, int index, int count, jobject bounds) { - jchar* textArray = env->GetCharArrayElements(text, NULL); + const jchar* textArray = env->GetCharArrayElements(text, NULL); doTextBounds(env, textArray + index, count, bounds, *paint); - env->ReleaseCharArrayElements(text, textArray, 0); + env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), + JNI_ABORT); } }; diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index 71dc875..eef8bb2 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -8,6 +8,12 @@ #include "SkTemplates.h" #include "SkXfermode.h" +static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) { + if (NULL == ptr) { + doThrowIAE(env); + } +} + static void Color_RGBToHSV(JNIEnv* env, jobject, int red, int green, int blue, jfloatArray hsvArray) { SkScalar hsv[3]; @@ -64,9 +70,11 @@ static void Shader_setLocalMatrix(JNIEnv* env, jobject, SkShader* shader, const static SkShader* BitmapShader_constructor(JNIEnv* env, jobject, const SkBitmap* bitmap, int tileModeX, int tileModeY) { - return SkShader::CreateBitmapShader(*bitmap, + SkShader* s = SkShader::CreateBitmapShader(*bitmap, (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY); + ThrowIAE_IfNull(env, s); + return s; } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -79,8 +87,8 @@ static SkShader* LinearGradient_create1(JNIEnv* env, jobject, pts[0].set(SkFloatToScalar(x0), SkFloatToScalar(y0)); pts[1].set(SkFloatToScalar(x1), SkFloatToScalar(y1)); - size_t count = env->GetArrayLength(colorArray); - int* colorValues = env->GetIntArrayElements(colorArray, NULL); + size_t count = env->GetArrayLength(colorArray); + const jint* colorValues = env->GetIntArrayElements(colorArray, NULL); SkAutoSTMalloc<8, SkScalar> storage(posArray ? count : 0); SkScalar* pos = NULL; @@ -93,9 +101,13 @@ static SkShader* LinearGradient_create1(JNIEnv* env, jobject, pos[i] = SkFloatToScalar(posValues[i]); } - SkShader* shader = SkGradientShader::CreateLinear(pts, (const SkColor*)colorValues, - pos, count, (SkShader::TileMode)tileMode); - env->ReleaseIntArrayElements(colorArray, colorValues, 0); + SkShader* shader = SkGradientShader::CreateLinear(pts, + reinterpret_cast<const SkColor*>(colorValues), + pos, count, + static_cast<SkShader::TileMode>(tileMode)); + env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), + JNI_ABORT); + ThrowIAE_IfNull(env, shader); return shader; } @@ -111,7 +123,9 @@ static SkShader* LinearGradient_create2(JNIEnv* env, jobject, colors[0] = color0; colors[1] = color1; - return SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode); + SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode); + ThrowIAE_IfNull(env, s); + return s; } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -123,8 +137,8 @@ static SkShader* RadialGradient_create1(JNIEnv* env, jobject, SkPoint center; center.set(SkFloatToScalar(x), SkFloatToScalar(y)); - size_t count = env->GetArrayLength(colorArray); - int* colorValues = env->GetIntArrayElements(colorArray, NULL); + size_t count = env->GetArrayLength(colorArray); + const jint* colorValues = env->GetIntArrayElements(colorArray, NULL); SkAutoSTMalloc<8, SkScalar> storage(posArray ? count : 0); SkScalar* pos = NULL; @@ -137,10 +151,15 @@ static SkShader* RadialGradient_create1(JNIEnv* env, jobject, pos[i] = SkFloatToScalar(posValues[i]); } - SkShader* shader = SkGradientShader::CreateRadial(center, SkFloatToScalar(radius), - (const SkColor*)colorValues, pos, - count, (SkShader::TileMode)tileMode); - env->ReleaseIntArrayElements(colorArray, colorValues, 0); + SkShader* shader = SkGradientShader::CreateRadial(center, + SkFloatToScalar(radius), + reinterpret_cast<const SkColor*>(colorValues), + pos, count, + static_cast<SkShader::TileMode>(tileMode)); + env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), + JNI_ABORT); + + ThrowIAE_IfNull(env, shader); return shader; } @@ -155,8 +174,10 @@ static SkShader* RadialGradient_create2(JNIEnv* env, jobject, colors[0] = color0; colors[1] = color1; - return SkGradientShader::CreateRadial(center, SkFloatToScalar(radius), colors, NULL, + SkShader* s = SkGradientShader::CreateRadial(center, SkFloatToScalar(radius), colors, NULL, 2, (SkShader::TileMode)tileMode); + ThrowIAE_IfNull(env, s); + return s; } /////////////////////////////////////////////////////////////////////////////// @@ -164,8 +185,8 @@ static SkShader* RadialGradient_create2(JNIEnv* env, jobject, static SkShader* SweepGradient_create1(JNIEnv* env, jobject, float x, float y, jintArray jcolors, jfloatArray jpositions) { - size_t count = env->GetArrayLength(jcolors); - int* colors = env->GetIntArrayElements(jcolors, NULL); + size_t count = env->GetArrayLength(jcolors); + const jint* colors = env->GetIntArrayElements(jcolors, NULL); SkAutoSTMalloc<8, SkScalar> storage(jpositions ? count : 0); SkScalar* pos = NULL; @@ -174,15 +195,18 @@ static SkShader* SweepGradient_create1(JNIEnv* env, jobject, float x, float y, AutoJavaFloatArray autoPos(env, jpositions, count); const float* posValues = autoPos.ptr(); pos = (SkScalar*)storage.get(); - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < count; i++) { pos[i] = SkFloatToScalar(posValues[i]); + } } SkShader* shader = SkGradientShader::CreateSweep(SkFloatToScalar(x), - SkFloatToScalar(y), - (const SkColor*)colors, - pos, count); - env->ReleaseIntArrayElements(jcolors, colors, 0); + SkFloatToScalar(y), + reinterpret_cast<const SkColor*>(colors), + pos, count); + env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors), + JNI_ABORT); + ThrowIAE_IfNull(env, shader); return shader; } @@ -192,8 +216,10 @@ static SkShader* SweepGradient_create2(JNIEnv* env, jobject, float x, float y, SkColor colors[2]; colors[0] = color0; colors[1] = color1; - return SkGradientShader::CreateSweep(SkFloatToScalar(x), SkFloatToScalar(y), + SkShader* s = SkGradientShader::CreateSweep(SkFloatToScalar(x), SkFloatToScalar(y), colors, NULL, 2); + ThrowIAE_IfNull(env, s); + return s; } /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h index e77cd30..c30ba22 100644 --- a/core/jni/android_bluetooth_common.h +++ b/core/jni/android_bluetooth_common.h @@ -61,6 +61,15 @@ jfieldID get_field(JNIEnv *env, (err)->name, (err)->message); \ dbus_error_free((err)); } +struct event_loop_native_data_t { + DBusConnection *conn; + /* These variables are set in waitForAndDispatchEventNative() and are + valid only within the scope of this function. At any other time, they + are NULL. */ + jobject me; + JNIEnv *env; +}; + dbus_bool_t dbus_func_args_async_valist(JNIEnv *env, DBusConnection *conn, int timeout_ms, diff --git a/core/jni/android_database_SQLiteProgram.cpp b/core/jni/android_database_SQLiteProgram.cpp index 54e7de2..7bda004 100644 --- a/core/jni/android_database_SQLiteProgram.cpp +++ b/core/jni/android_database_SQLiteProgram.cpp @@ -172,7 +172,7 @@ static void native_bind_blob(JNIEnv* env, jobject object, if (err != SQLITE_OK) { char buf[32]; - sprintf(buf, "handle %p", statement); + sprintf(buf, "statement %p", statement); throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); return; } diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp index 9458433..4427168 100644 --- a/core/jni/android_database_SQLiteQuery.cpp +++ b/core/jni/android_database_SQLiteQuery.cpp @@ -106,11 +106,12 @@ static int finish_program_and_get_row_count(sqlite3_stmt *statement) { } static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, - jint startPos, jint offsetParam) + jint startPos, jint offsetParam, jint maxRead, jint lastPos) { int err; sqlite3_stmt * statement = GET_STATEMENT(env, object); - int numRows = 0; + int numRows = lastPos; + maxRead += lastPos; int numColumns; int retryCount; int boundParams; @@ -168,7 +169,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, } } - while(true) { + while(startPos != 0 || numRows < maxRead) { err = sqlite3_step(statement); if (err == SQLITE_ROW) { LOG_WINDOW("\nStepped statement %p to row %d", statement, startPos + numRows); @@ -274,6 +275,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, if (i < numColumns) { // Not all the fields fit in the window + // Unknown data error happened break; } @@ -305,8 +307,12 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, LOG_WINDOW("Resetting statement %p after fetching %d rows in %d bytes\n\n\n\n", statement, numRows, window->size() - window->freeSpace()); // LOGI("Filled window with %d rows in %d bytes", numRows, window->size() - window->freeSpace()); - sqlite3_reset(statement); - return startPos + numRows; + if (err == SQLITE_ROW) { + return -1; + } else { + sqlite3_reset(statement); + return startPos + numRows; + } } static jint native_column_count(JNIEnv* env, jobject object) @@ -330,7 +336,7 @@ static jstring native_column_name(JNIEnv* env, jobject object, jint columnIndex) static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ - {"native_fill_window", "(Landroid/database/CursorWindow;II)I", (void *)native_fill_window}, + {"native_fill_window", "(Landroid/database/CursorWindow;IIII)I", (void *)native_fill_window}, {"native_column_count", "()I", (void*)native_column_count}, {"native_column_name", "(I)Ljava/lang/String;", (void *)native_column_name}, }; diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index a29c27b..4b126a6 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -2,16 +2,16 @@ ** ** Copyright 2008, 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. */ @@ -50,15 +50,17 @@ struct fields_t { }; static fields_t fields; +static Mutex sLock; struct callback_cookie { jclass camera_class; jobject camera_ref; }; -static Camera *get_native_camera(JNIEnv *env, jobject thiz) +sp<Camera> get_native_camera(JNIEnv *env, jobject thiz) { - Camera *c = reinterpret_cast<Camera*>(env->GetIntField(thiz, fields.context)); + Mutex::Autolock _l(sLock); + sp<Camera> c = reinterpret_cast<Camera*>(env->GetIntField(thiz, fields.context)); if (c == 0) jniThrowException(env, "java/lang/RuntimeException", "Method called after release()"); @@ -80,7 +82,7 @@ static void err_callback(status_t err, void *cookie) break; } LOGV("err_callback: camera_ref=%x, cookie=%x", (int)c->camera_ref, (int)cookie); - + env->CallStaticVoidMethod(c->camera_class, fields.post_event, c->camera_ref, kErrorCallback, error, 0, NULL); } @@ -117,34 +119,38 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj env->SetIntField(thiz, fields.listener_context, (int)cookie); LOGV("native_setup: camera_ref=%x, camera_obj=%x, cookie=%x", (int)cookie->camera_ref, (int)thiz, (int)cookie); - + // save camera object in opaque field env->SetIntField(thiz, fields.context, reinterpret_cast<int>(c.get())); c->setErrorCallback(err_callback, cookie); - - // hold a strong reference so this doesn't go away while the app is still running + + // hold a strong reference so the camera doesn't go away while the app is still running c->incStrong(thiz); } // disconnect from camera service static void android_hardware_Camera_release(JNIEnv *env, jobject thiz) { + Mutex::Autolock _l(sLock); sp<Camera> c = reinterpret_cast<Camera*>(env->GetIntField(thiz, fields.context)); // It's okay to call this when the native camera context is already null. // This handles the case where the user has called release() and the // finalizer is invoked later. if (c != 0) { + // Make sure that we do not attempt to deliver an eror callback on a deleted + // Java object. + c->setErrorCallback(NULL, NULL); c->disconnect(); // remove our strong reference created in native setup c->decStrong(thiz); env->SetIntField(thiz, fields.context, 0); - + callback_cookie *cookie = (callback_cookie *)env->GetIntField(thiz, fields.listener_context); LOGV("release: camera_ref=%x, camera_obj=%x, cookie=%x", (int)cookie->camera_ref, (int)thiz, (int)cookie); - + if (cookie) { env->DeleteGlobalRef(cookie->camera_ref); env->DeleteGlobalRef(cookie->camera_class); @@ -156,7 +162,7 @@ static void android_hardware_Camera_release(JNIEnv *env, jobject thiz) static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, jobject surface) { - Camera *c = get_native_camera(env, thiz); + sp<Camera> c = get_native_camera(env, thiz); if (c == 0) return; @@ -183,6 +189,7 @@ static void preview_callback(const sp<IMemory>& mem, void *cookie) jbyteArray array = env->NewByteArray(size); if (array == NULL) { LOGE("Couldn't allocate byte array for YUV data"); + env->ExceptionClear(); return; } @@ -199,10 +206,10 @@ static void preview_callback(const sp<IMemory>& mem, void *cookie) static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz) { - Camera *c = get_native_camera(env, thiz); + sp<Camera> c = get_native_camera(env, thiz); if (c == 0) return; - + if (c->startPreview() != NO_ERROR) { jniThrowException(env, "java/io/IOException", "startPreview failed"); return; @@ -211,7 +218,7 @@ static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz) static void android_hardware_Camera_stopPreview(JNIEnv *env, jobject thiz) { - Camera *c = get_native_camera(env, thiz); + sp<Camera> c = get_native_camera(env, thiz); if (c == 0) return; @@ -220,7 +227,7 @@ static void android_hardware_Camera_stopPreview(JNIEnv *env, jobject thiz) static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed) { - Camera *c = get_native_camera(env, thiz); + sp<Camera> c = get_native_camera(env, thiz); if (c == 0) return; @@ -228,7 +235,10 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy // each preview frame for nothing. callback_cookie *cookie = (callback_cookie *)env->GetIntField(thiz, fields.listener_context); - c->setFrameCallback(installed ? preview_callback : NULL, cookie); + + c->setFrameCallback(installed ? preview_callback : NULL, + cookie, + installed ? FRAME_CALLBACK_FLAG_CAMERA: FRAME_CALLBACK_FLAG_NOOP); } static void autofocus_callback_impl(bool success, void *cookie) @@ -236,7 +246,7 @@ static void autofocus_callback_impl(bool success, void *cookie) JNIEnv *env = AndroidRuntime::getJNIEnv(); callback_cookie *c = (callback_cookie *)cookie; env->CallStaticVoidMethod(c->camera_class, fields.post_event, - c->camera_ref, kAutoFocusCallback, + c->camera_ref, kAutoFocusCallback, success, 0, NULL); } @@ -244,7 +254,7 @@ static void autofocus_callback_impl(bool success, void *cookie) static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz) { - Camera *c = get_native_camera(env, thiz); + sp<Camera> c = get_native_camera(env, thiz); if (c == 0) return; callback_cookie *cookie = (callback_cookie *)env->GetIntField(thiz, fields.listener_context); @@ -282,6 +292,7 @@ static void jpeg_callback(const sp<IMemory>& mem, void *cookie) jbyteArray array = env->NewByteArray(size); if (array == NULL) { LOGE("Couldn't allocate byte array for JPEG data"); + env->ExceptionClear(); return; } @@ -315,7 +326,7 @@ static void raw_callback(const sp<IMemory>& mem __attribute__((unused)), static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz) { - Camera *c = get_native_camera(env, thiz); + sp<Camera> c = get_native_camera(env, thiz); if (c == 0) return; @@ -334,7 +345,7 @@ static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz) static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params) { - Camera *c = get_native_camera(env, thiz); + sp<Camera> c = get_native_camera(env, thiz); if (c == 0) return; @@ -345,20 +356,32 @@ static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jst env->ReleaseStringCritical(params, str); } if (c->setParameters(params8) != NO_ERROR) { - jniThrowException(env, "java/io/IllegalArgumentException", "setParameters failed"); + jniThrowException(env, "java/lang/IllegalArgumentException", "setParameters failed"); return; } } static jstring android_hardware_Camera_getParameters(JNIEnv *env, jobject thiz) { - Camera *c = get_native_camera(env, thiz); + sp<Camera> c = get_native_camera(env, thiz); if (c == 0) return 0; return env->NewStringUTF(c->getParameters().string()); } +static void android_hardware_Camera_reconnect(JNIEnv *env, jobject thiz) +{ + sp<Camera> c = get_native_camera(env, thiz); + if (c == 0) + return; + + if (c->reconnect() != NO_ERROR) { + jniThrowException(env, "java/io/IOException", "reconnect failed"); + return; + } +} + //------------------------------------------------- static JNINativeMethod camMethods[] = { @@ -391,7 +414,10 @@ static JNINativeMethod camMethods[] = { (void *)android_hardware_Camera_setParameters }, { "native_getParameters", "()Ljava/lang/String;", - (void *)android_hardware_Camera_getParameters } + (void *)android_hardware_Camera_getParameters }, + { "reconnect", + "()V", + (void*)android_hardware_Camera_reconnect }, }; struct field { @@ -442,7 +468,7 @@ int register_android_hardware_Camera(JNIEnv *env) LOGE("Can't find android/hardware/Camera.postEventFromNative"); return -1; } - + // Register native functions return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera", diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index e8dcd71..75aa458 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -24,51 +24,142 @@ namespace android { +struct SensorOffsets +{ + jfieldID name; + jfieldID vendor; + jfieldID version; + jfieldID handle; + jfieldID type; + jfieldID range; + jfieldID resolution; + jfieldID power; +} gSensorOffsets; + /* * The method below are not thread-safe and not intended to be */ +static sensors_module_t* sSensorModule = 0; +static sensors_data_device_t* sSensorDevice = 0; + +static jint +sensors_module_init(JNIEnv *env, jclass clazz) +{ + int err = 0; + sensors_module_t const* module; + err = hw_get_module(SENSORS_HARDWARE_MODULE_ID, (const hw_module_t **)&module); + if (err == 0) + sSensorModule = (sensors_module_t*)module; + return err; +} + static jint -android_data_open(JNIEnv *env, jclass clazz, jobject fdo) +sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint next) +{ + if (sSensorModule == NULL) + return 0; + + SensorOffsets& sensorOffsets = gSensorOffsets; + const struct sensor_t* list; + int count = sSensorModule->get_sensors_list(sSensorModule, &list); + if (next >= count) + return -1; + + list += next; + + jstring name = env->NewStringUTF(list->name); + jstring vendor = env->NewStringUTF(list->vendor); + env->SetObjectField(sensor, sensorOffsets.name, name); + env->SetObjectField(sensor, sensorOffsets.vendor, vendor); + env->SetIntField(sensor, sensorOffsets.version, list->version); + env->SetIntField(sensor, sensorOffsets.handle, list->handle); + env->SetIntField(sensor, sensorOffsets.type, list->type); + env->SetFloatField(sensor, sensorOffsets.range, list->maxRange); + env->SetFloatField(sensor, sensorOffsets.resolution, list->resolution); + env->SetFloatField(sensor, sensorOffsets.power, list->power); + + next++; + return next<count ? next : 0; +} + +//---------------------------------------------------------------------------- +static jint +sensors_data_init(JNIEnv *env, jclass clazz) +{ + if (sSensorModule == NULL) + return -1; + int err = sensors_data_open(&sSensorModule->common, &sSensorDevice); + return err; +} + +static jint +sensors_data_uninit(JNIEnv *env, jclass clazz) +{ + int err = 0; + if (sSensorDevice) { + err = sensors_data_close(sSensorDevice); + if (err == 0) + sSensorDevice = 0; + } + return err; +} + +static jint +sensors_data_open(JNIEnv *env, jclass clazz, jobject fdo) { jclass FileDescriptor = env->FindClass("java/io/FileDescriptor"); jfieldID offset = env->GetFieldID(FileDescriptor, "descriptor", "I"); int fd = env->GetIntField(fdo, offset); - return sensors_data_open(fd); // doesn't take ownership of fd + return sSensorDevice->data_open(sSensorDevice, fd); // doesn't take ownership of fd } static jint -android_data_close(JNIEnv *env, jclass clazz) +sensors_data_close(JNIEnv *env, jclass clazz) { - return sensors_data_close(); + return sSensorDevice->data_close(sSensorDevice); } static jint -android_data_poll(JNIEnv *env, jclass clazz, jfloatArray values, jint sensors) +sensors_data_poll(JNIEnv *env, jclass clazz, + jfloatArray values, jintArray status, jlongArray timestamp) { sensors_data_t data; - int res = sensors_data_poll(&data, sensors); - if (res) { + int res = sSensorDevice->poll(sSensorDevice, &data); + if (res >= 0) { + jint accuracy = data.vector.status; env->SetFloatArrayRegion(values, 0, 3, data.vector.v); - // return the sensor's number - res = 31 - __builtin_clz(res); - // and its status in the top 4 bits - res |= data.vector.status << 28; + env->SetIntArrayRegion(status, 0, 1, &accuracy); + env->SetLongArrayRegion(timestamp, 0, 1, &data.time); } return res; } -static jint -android_data_get_sensors(JNIEnv *env, jclass clazz) +static void +nativeClassInit (JNIEnv *_env, jclass _this) { - return sensors_data_get_sensors(); + jclass sensorClass = _env->FindClass("android/hardware/Sensor"); + SensorOffsets& sensorOffsets = gSensorOffsets; + sensorOffsets.name = _env->GetFieldID(sensorClass, "mName", "Ljava/lang/String;"); + sensorOffsets.vendor = _env->GetFieldID(sensorClass, "mVendor", "Ljava/lang/String;"); + sensorOffsets.version = _env->GetFieldID(sensorClass, "mVersion", "I"); + sensorOffsets.handle = _env->GetFieldID(sensorClass, "mHandle", "I"); + sensorOffsets.type = _env->GetFieldID(sensorClass, "mType", "I"); + sensorOffsets.range = _env->GetFieldID(sensorClass, "mMaxRange", "F"); + sensorOffsets.resolution = _env->GetFieldID(sensorClass, "mResolution","F"); + sensorOffsets.power = _env->GetFieldID(sensorClass, "mPower", "F"); } static JNINativeMethod gMethods[] = { - {"_sensors_data_open", "(Ljava/io/FileDescriptor;)I", (void*) android_data_open }, - {"_sensors_data_close", "()I", (void*) android_data_close }, - {"_sensors_data_poll", "([FI)I", (void*) android_data_poll }, - {"_sensors_data_get_sensors","()I", (void*) android_data_get_sensors }, + {"nativeClassInit", "()V", (void*)nativeClassInit }, + {"sensors_module_init","()I", (void*)sensors_module_init }, + {"sensors_module_get_next_sensor","(Landroid/hardware/Sensor;I)I", + (void*)sensors_module_get_next_sensor }, + {"sensors_data_init", "()I", (void*)sensors_data_init }, + {"sensors_data_uninit", "()I", (void*)sensors_data_uninit }, + {"sensors_data_open", "(Ljava/io/FileDescriptor;)I", (void*)sensors_data_open }, + {"sensors_data_close", "()I", (void*)sensors_data_close }, + {"sensors_data_poll", "([F[I[J)I", (void*)sensors_data_poll }, }; }; // namespace android diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp new file mode 100644 index 0000000..e4586d9 --- /dev/null +++ b/core/jni/android_media_AudioRecord.cpp @@ -0,0 +1,566 @@ +/* + * Copyright (C) 2008 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_NDEBUG 0 + +#define LOG_TAG "AudioRecord-JNI" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <math.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include "utils/Log.h" +#include "media/AudioSystem.h" +#include "media/AudioRecord.h" + + +// ---------------------------------------------------------------------------- + +using namespace android; + +// ---------------------------------------------------------------------------- +static const char* const kClassPathName = "android/media/AudioRecord"; + +struct fields_t { + // these fields provide access from C++ to the... + jclass audioRecordClass; //... AudioRecord class + jmethodID postNativeEventInJava; //... event post callback method + int PCM16; //... format constants + int PCM8; //... format constants + int SOURCE_DEFAULT; //... record source constants + int SOURCE_MIC; //... record source constants + jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object + jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data +}; +static fields_t javaAudioRecordFields; + +struct audiorecord_callback_cookie { + jclass audioRecord_class; + jobject audioRecord_ref; + }; + +// ---------------------------------------------------------------------------- + +#define AUDIORECORD_SUCCESS 0 +#define AUDIORECORD_ERROR -1 +#define AUDIORECORD_ERROR_BAD_VALUE -2 +#define AUDIORECORD_ERROR_INVALID_OPERATION -3 +#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -4 +#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT -5 +#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -6 +#define AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE -7 +#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -8 + +jint android_media_translateRecorderErrorCode(int code) { + switch(code) { + case NO_ERROR: + return AUDIORECORD_SUCCESS; + case BAD_VALUE: + return AUDIORECORD_ERROR_BAD_VALUE; + case INVALID_OPERATION: + return AUDIORECORD_ERROR_INVALID_OPERATION; + default: + return AUDIORECORD_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static void recorderCallback(int event, void* user, void *info) { + if (event == AudioRecord::EVENT_MORE_DATA) { + // set size to 0 to signal we're not using the callback to read more data + AudioRecord::Buffer* pBuff = (AudioRecord::Buffer*)info; + pBuff->size = 0; + + } else if (event == AudioRecord::EVENT_MARKER) { + audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user; + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (user && env) { + env->CallStaticVoidMethod( + callbackInfo->audioRecord_class, + javaAudioRecordFields.postNativeEventInJava, + callbackInfo->audioRecord_ref, event, 0,0, NULL); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } + + } else if (event == AudioRecord::EVENT_NEW_POS) { + audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user; + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (user && env) { + env->CallStaticVoidMethod( + callbackInfo->audioRecord_class, + javaAudioRecordFields.postNativeEventInJava, + callbackInfo->audioRecord_ref, event, 0,0, NULL); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } + } +} + + +// ---------------------------------------------------------------------------- +static int +android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, + jint source, jint sampleRateInHertz, jint nbChannels, + jint audioFormat, jint buffSizeInBytes) +{ + //LOGV(">> Entering android_media_AudioRecord_setup"); + //LOGV("sampleRate=%d, audioFormat=%d, nbChannels=%d, buffSizeInBytes=%d", + // sampleRateInHertz, audioFormat, nbChannels, buffSizeInBytes); + + if ((nbChannels == 0) || (nbChannels > 2)) { + LOGE("Error creating AudioRecord: channel count is not 1 or 2."); + return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT; + } + + // compare the format against the Java constants + if ((audioFormat != javaAudioRecordFields.PCM16) + && (audioFormat != javaAudioRecordFields.PCM8)) { + LOGE("Error creating AudioRecord: unsupported audio format."); + return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT; + } + + int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1; + int format = audioFormat==javaAudioRecordFields.PCM16 ? + AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT; + + if (buffSizeInBytes == 0) { + LOGE("Error creating AudioRecord: frameCount is 0."); + return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT; + } + int frameSize = nbChannels * bytesPerSample; + size_t frameCount = buffSizeInBytes / frameSize; + + // compare the source against the Java constants + AudioRecord::stream_type arSource; + if (source == javaAudioRecordFields.SOURCE_DEFAULT) { + arSource = AudioRecord::DEFAULT_INPUT; + } else if (source == javaAudioRecordFields.SOURCE_MIC) { + arSource = AudioRecord::MIC_INPUT; + } else { + LOGE("Error creating AudioRecord: unknown source."); + return AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE; + } + + audiorecord_callback_cookie *lpCallbackData = NULL; + AudioRecord* lpRecorder = NULL; + + // create an uninitialized AudioRecord object + lpRecorder = new AudioRecord(); + if(lpRecorder == NULL) { + LOGE("Error creating AudioRecord instance."); + return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; + } + + // create the callback information: + // this data will be passed with every AudioRecord callback + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + LOGE("Can't find %s when setting up callback.", kClassPathName); + goto native_track_failure; + } + lpCallbackData = new audiorecord_callback_cookie; + lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); + // we use a weak reference so the AudioRecord object can be garbage collected. + lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); + + lpRecorder->set(arSource, + sampleRateInHertz, + format, // word length, PCM + nbChannels, + frameCount, + 0, // flags + recorderCallback,// callback_t + lpCallbackData,// void* user + 0, // notificationFrames, + true); // threadCanCallJava) + + if(lpRecorder->initCheck() != NO_ERROR) { + LOGE("Error creating AudioRecord instance: initialization check failed."); + goto native_init_failure; + } + + // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field + // of the Java object + env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)lpRecorder); + + // save our newly created callback information in the "nativeCallbackCookie" field + // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize() + env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData); + + return AUDIORECORD_SUCCESS; + + // failure: +native_init_failure: + delete lpCallbackData; + +native_track_failure: + delete lpRecorder; + + env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0); + env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); + + return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; +} + + + +// ---------------------------------------------------------------------------- +static void +android_media_AudioRecord_start(JNIEnv *env, jobject thiz) +{ + AudioRecord *lpRecorder = + (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); + if (lpRecorder == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + lpRecorder->start(); +} + + +// ---------------------------------------------------------------------------- +static void +android_media_AudioRecord_stop(JNIEnv *env, jobject thiz) +{ + AudioRecord *lpRecorder = + (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); + if (lpRecorder == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + lpRecorder->stop(); + //LOGV("Called lpRecorder->stop()"); +} + + +// ---------------------------------------------------------------------------- +static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { + + // delete the AudioRecord object + AudioRecord *lpRecorder = + (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); + + if (lpRecorder) { + //LOGV("About to delete lpRecorder: %x\n", (int)lpRecorder); + lpRecorder->stop(); + delete lpRecorder; + } + + // delete the callback information + audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField( + thiz, javaAudioRecordFields.nativeCallbackCookie); + if (lpCookie) { + LOGV("deleting lpCookie: %x\n", (int)lpCookie); + delete lpCookie; + } + +} + + +// ---------------------------------------------------------------------------- +static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) { + + // do everything a call to finalize would + android_media_AudioRecord_finalize(env, thiz); + // + reset the native resources in the Java object so any attempt to access + // them after a call to release fails. + env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0); + env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz, + jbyteArray javaAudioData, + jint offsetInBytes, jint sizeInBytes) { + jbyte* recordBuff = NULL; + AudioRecord *lpRecorder = NULL; + + // get the audio recorder from which we'll read new audio samples + lpRecorder = + (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); + if (lpRecorder == NULL) { + LOGE("Unable to retrieve AudioRecord object, can't record"); + return 0; + } + + if (!javaAudioData) { + LOGE("Invalid Java array to store recorded audio, can't record"); + return 0; + } + + // get the pointer to where we'll record the audio + recordBuff = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL); + + if (recordBuff == NULL) { + LOGE("Error retrieving destination for recorded audio data, can't record"); + return 0; + } + + // read the new audio data from the native AudioRecord object + ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize(); + ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, + sizeInBytes > (jint)recorderBuffSize ? + (jint)recorderBuffSize : sizeInBytes ); + env->ReleasePrimitiveArrayCritical(javaAudioData, recordBuff, 0); + + return (jint) readSize; +} + +// ---------------------------------------------------------------------------- +static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz, + jshortArray javaAudioData, + jint offsetInShorts, jint sizeInShorts) { + + return (android_media_AudioRecord_readInByteArray(env, thiz, + (jbyteArray) javaAudioData, + offsetInShorts*2, sizeInShorts*2) + / 2); +} + +// ---------------------------------------------------------------------------- +static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, + jobject jBuffer, jint sizeInBytes) { + AudioRecord *lpRecorder = NULL; + //LOGV("Entering android_media_AudioRecord_readInBuffer"); + + // get the audio recorder from which we'll read new audio samples + lpRecorder = + (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); + if(lpRecorder==NULL) + return 0; + + // direct buffer and direct access supported? + long capacity = env->GetDirectBufferCapacity(jBuffer); + if(capacity == -1) { + // buffer direct access is not supported + LOGE("Buffer direct access is not supported, can't record"); + return 0; + } + //LOGV("capacity = %ld", capacity); + jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); + if(nativeFromJavaBuf==NULL) { + LOGE("Buffer direct access is not supported, can't record"); + return 0; + } + + // read new data from the recorder + return (jint) lpRecorder->read(nativeFromJavaBuf, + capacity < sizeInBytes ? capacity : sizeInBytes); +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz, + jint markerPos) { + + AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField( + thiz, javaAudioRecordFields.nativeRecorderInJavaObj); + + if (lpRecorder) { + return + android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) ); + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioRecord pointer for setMarkerPosition()"); + return AUDIORECORD_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) { + + AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField( + thiz, javaAudioRecordFields.nativeRecorderInJavaObj); + uint32_t markerPos = 0; + + if (lpRecorder) { + lpRecorder->getMarkerPosition(&markerPos); + return (jint)markerPos; + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioRecord pointer for getMarkerPosition()"); + return AUDIORECORD_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz, + jint period) { + + AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField( + thiz, javaAudioRecordFields.nativeRecorderInJavaObj); + + if (lpRecorder) { + return + android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) ); + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()"); + return AUDIORECORD_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) { + + AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField( + thiz, javaAudioRecordFields.nativeRecorderInJavaObj); + uint32_t period = 0; + + if (lpRecorder) { + lpRecorder->getPositionUpdatePeriod(&period); + return (jint)period; + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()"); + return AUDIORECORD_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +static JNINativeMethod gMethods[] = { + // name, signature, funcPtr + {"native_start", "()V", (void *)android_media_AudioRecord_start}, + {"native_stop", "()V", (void *)android_media_AudioRecord_stop}, + {"native_setup", "(Ljava/lang/Object;IIIII)I", + (void *)android_media_AudioRecord_setup}, + {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, + {"native_release", "()V", (void *)android_media_AudioRecord_release}, + {"native_read_in_byte_array", + "([BII)I", (void *)android_media_AudioRecord_readInByteArray}, + {"native_read_in_short_array", + "([SII)I", (void *)android_media_AudioRecord_readInShortArray}, + {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I", + (void *)android_media_AudioRecord_readInDirectBuffer}, + {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos}, + {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos}, + {"native_set_pos_update_period", + "(I)I", (void *)android_media_AudioRecord_set_pos_update_period}, + {"native_get_pos_update_period", + "()I", (void *)android_media_AudioRecord_get_pos_update_period}, +}; + +// field names found in android/media/AudioRecord.java +#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" +#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT" +#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" +#define JAVA_CONST_SOURCEDEFAULT_NAME "SOURCE_DEFAULT" +#define JAVA_CONST_SOURCEMIC_NAME "SOURCE_MIC" +#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" +#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" + +#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat" + +// ---------------------------------------------------------------------------- + +extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv, + jclass theClass, const char* className, const char* constName, int* constVal); + +// ---------------------------------------------------------------------------- +int register_android_media_AudioRecord(JNIEnv *env) +{ + javaAudioRecordFields.audioRecordClass = NULL; + javaAudioRecordFields.postNativeEventInJava = NULL; + javaAudioRecordFields.nativeRecorderInJavaObj = NULL; + javaAudioRecordFields.nativeCallbackCookie = NULL; + + + // Get the AudioRecord class + javaAudioRecordFields.audioRecordClass = env->FindClass(kClassPathName); + if (javaAudioRecordFields.audioRecordClass == NULL) { + LOGE("Can't find %s", kClassPathName); + return -1; + } + + // Get the postEvent method + javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID( + javaAudioRecordFields.audioRecordClass, + JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V"); + if (javaAudioRecordFields.postNativeEventInJava == NULL) { + LOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME); + return -1; + } + + // Get the variables + // mNativeRecorderInJavaObj + javaAudioRecordFields.nativeRecorderInJavaObj = + env->GetFieldID(javaAudioRecordFields.audioRecordClass, + JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I"); + if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) { + LOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME); + return -1; + } + // mNativeCallbackCookie + javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID( + javaAudioRecordFields.audioRecordClass, + JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I"); + if (javaAudioRecordFields.nativeCallbackCookie == NULL) { + LOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME); + return -1; + } + + // Get the format constants from the AudioFormat class + jclass audioFormatClass = NULL; + audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME); + if (audioFormatClass == NULL) { + LOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME); + return -1; + } + if ( !android_media_getIntConstantFromClass(env, audioFormatClass, + JAVA_AUDIOFORMAT_CLASS_NAME, + JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16)) + || !android_media_getIntConstantFromClass(env, audioFormatClass, + JAVA_AUDIOFORMAT_CLASS_NAME, + JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) { + // error log performed in getIntConstantFromClass() + return -1; + } + + // Get the recording source constants from the AudioRecord class + if ( !android_media_getIntConstantFromClass(env, javaAudioRecordFields.audioRecordClass, + kClassPathName, + JAVA_CONST_SOURCEDEFAULT_NAME, &(javaAudioRecordFields.SOURCE_DEFAULT)) + || !android_media_getIntConstantFromClass(env, javaAudioRecordFields.audioRecordClass, + kClassPathName, + JAVA_CONST_SOURCEMIC_NAME, &(javaAudioRecordFields.SOURCE_MIC)) ) { + // error log performed in getIntConstantFromClass() + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + kClassPathName, gMethods, NELEM(gMethods)); +} + +// ---------------------------------------------------------------------------- diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp new file mode 100644 index 0000000..4a98fcc --- /dev/null +++ b/core/jni/android_media_AudioTrack.cpp @@ -0,0 +1,832 @@ +/* + * Copyright (C) 2008 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_NDEBUG 0 + +#define LOG_TAG "AudioTrack-JNI" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <math.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include "utils/Log.h" +#include "media/AudioSystem.h" +#include "media/AudioTrack.h" + +#include <utils/MemoryHeapBase.h> +#include <utils/MemoryBase.h> + + +// ---------------------------------------------------------------------------- + +using namespace android; + +// ---------------------------------------------------------------------------- +static const char* const kClassPathName = "android/media/AudioTrack"; + +struct fields_t { + // these fields provide access from C++ to the... + jclass audioTrackClass; //... AudioTrack class + jmethodID postNativeEventInJava; //... event post callback method + int PCM16; //... format constants + int PCM8; //... format constants + int STREAM_VOICE_CALL; //... stream type constants + int STREAM_SYSTEM; //... stream type constants + int STREAM_RING; //... stream type constants + int STREAM_MUSIC; //... stream type constants + int STREAM_ALARM; //... stream type constants + int MODE_STREAM; //... memory mode + int MODE_STATIC; //... memory mode + 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; + +struct audiotrack_callback_cookie { + jclass audioTrack_class; + jobject audioTrack_ref; + }; + +// ---------------------------------------------------------------------------- +class AudioTrackJniStorage { + public: + sp<MemoryHeapBase> mMemHeap; + sp<MemoryBase> mMemBase; + audiotrack_callback_cookie mCallbackData; + + AudioTrackJniStorage() { + } + + ~AudioTrackJniStorage() { + mMemBase.clear(); + mMemHeap.clear(); + } + + bool allocSharedMem(int sizeInBytes) { + mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base"); + if (mMemHeap->getHeapID() < 0) { + return false; + } + mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes); + return true; + } +}; + +// ---------------------------------------------------------------------------- +#define DEFAULT_OUTPUT_SAMPLE_RATE 44100 + +#define AUDIOTRACK_SUCCESS 0 +#define AUDIOTRACK_ERROR -1 +#define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM -2 +#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT -3 +#define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT -4 +#define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE -5 +#define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED -6 +#define AUDIOTRACK_ERROR_BAD_VALUE -7 +#define AUDIOTRACK_ERROR_INVALID_OPERATION -8 + + +jint android_media_translateErrorCode(int code) { + switch(code) { + case NO_ERROR: + return AUDIOTRACK_SUCCESS; + case BAD_VALUE: + return AUDIOTRACK_ERROR_BAD_VALUE; + case INVALID_OPERATION: + return AUDIOTRACK_ERROR_INVALID_OPERATION; + default: + return AUDIOTRACK_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static void audioCallback(int event, void* user, void *info) { + if (event == AudioTrack::EVENT_MORE_DATA) { + // set size to 0 to signal we're not using the callback to write more data + AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info; + pBuff->size = 0; + + } else if (event == AudioTrack::EVENT_MARKER) { + audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user; + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (user && env) { + env->CallStaticVoidMethod( + callbackInfo->audioTrack_class, + javaAudioTrackFields.postNativeEventInJava, + callbackInfo->audioTrack_ref, event, 0,0, NULL); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } + + } else if (event == AudioTrack::EVENT_NEW_POS) { + audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user; + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (user && env) { + env->CallStaticVoidMethod( + callbackInfo->audioTrack_class, + javaAudioTrackFields.postNativeEventInJava, + callbackInfo->audioTrack_ref, event, 0,0, NULL); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } + } +} + + +// ---------------------------------------------------------------------------- +static int +android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, + jint streamType, jint sampleRateInHertz, jint nbChannels, + jint audioFormat, jint buffSizeInBytes, jint memoryMode) +{ + //LOGV("sampleRate=%d, audioFormat(from Java)=%d, nbChannels=%d, buffSize=%d", + // sampleRateInHertz, audioFormat, nbChannels, buffSizeInBytes); + int afSampleRate; + int afFrameCount; + + if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + LOGE("Error creating AudioTrack: Could not get AudioSystem frame count."); + return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; + } + if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { + LOGE("Error creating AudioTrack: Could not get AudioSystem sampling rate."); + 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; + } + + // check the stream type + AudioTrack::stream_type atStreamType; + if (streamType == javaAudioTrackFields.STREAM_VOICE_CALL) { + atStreamType = AudioTrack::VOICE_CALL; + } else if (streamType == javaAudioTrackFields.STREAM_SYSTEM) { + atStreamType = AudioTrack::SYSTEM; + } else if (streamType == javaAudioTrackFields.STREAM_RING) { + atStreamType = AudioTrack::RING; + } else if (streamType == javaAudioTrackFields.STREAM_MUSIC) { + atStreamType = AudioTrack::MUSIC; + } else if (streamType == javaAudioTrackFields.STREAM_ALARM) { + atStreamType = AudioTrack::ALARM; + } else { + LOGE("Error creating AudioTrack: unknown stream type."); + return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE; + } + + // check the format. + // This function was called from Java, so we compare the format against the Java constants + if ((audioFormat != javaAudioTrackFields.PCM16) && (audioFormat != javaAudioTrackFields.PCM8)) { + LOGE("Error creating AudioTrack: unsupported audio format."); + return AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT; + } + + // compute the frame count + 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); + } + + AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage(); + + // initialize the callback information: + // this data will be passed with every AudioTrack callback + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + LOGE("Can't find %s when setting up callback.", kClassPathName); + delete lpJniStorage; + return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; + } + lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); + // we use a weak reference so the AudioTrack object can be garbage collected. + lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); + + // create the native AudioTrack object + AudioTrack* lpTrack = new AudioTrack(); + if (lpTrack == NULL) { + LOGE("Error creating uninitialized AudioTrack"); + goto native_track_failure; + } + + // initialize the native AudioTrack object + if (memoryMode == javaAudioTrackFields.MODE_STREAM) { + + lpTrack->set( + atStreamType,// stream type + sampleRateInHertz, + format,// word length, PCM + nbChannels, + frameCount, + 0,// flags + audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) + 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack + 0,// shared mem + true);// thread can call Java + + } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) { + // AudioTrack is using shared memory + + if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { + LOGE("Error creating AudioTrack in static mode: error creating mem heap base"); + goto native_init_failure; + } + + lpTrack->set( + atStreamType,// stream type + sampleRateInHertz, + format,// word length, PCM + nbChannels, + frameCount, + 0,// flags + audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); + 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack + lpJniStorage->mMemBase,// shared mem + true);// thread can call Java + } + + if (lpTrack->initCheck() != NO_ERROR) { + LOGE("Error initializing AudioTrack"); + goto native_init_failure; + } + + // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field + // of the Java object (in mNativeTrackInJavaObj) + env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack); + + // save the JNI resources so we can free them later + //LOGV("storing lpJniStorage: %x\n", (int)lpJniStorage); + env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage); + + return AUDIOTRACK_SUCCESS; + + // failures: +native_init_failure: + delete lpTrack; + env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0); + +native_track_failure: + delete lpJniStorage; + env->SetIntField(thiz, javaAudioTrackFields.jniData, 0); + return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; + +} + + +// ---------------------------------------------------------------------------- +static void +android_media_AudioTrack_start(JNIEnv *env, jobject thiz) +{ + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + if (lpTrack == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for start()"); + } + + lpTrack->start(); +} + + +// ---------------------------------------------------------------------------- +static void +android_media_AudioTrack_stop(JNIEnv *env, jobject thiz) +{ + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + if (lpTrack == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for stop()"); + } + + lpTrack->stop(); +} + + +// ---------------------------------------------------------------------------- +static void +android_media_AudioTrack_pause(JNIEnv *env, jobject thiz) +{ + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + if (lpTrack == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for pause()"); + } + + lpTrack->pause(); +} + + +// ---------------------------------------------------------------------------- +static void +android_media_AudioTrack_flush(JNIEnv *env, jobject thiz) +{ + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + if (lpTrack == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for flush()"); + } + + lpTrack->flush(); +} + +// ---------------------------------------------------------------------------- +static void +android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol ) +{ + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + if (lpTrack == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for setVolume()"); + } + + lpTrack->setVolume(leftVol, rightVol); +} + +// ---------------------------------------------------------------------------- +static void android_media_AudioTrack_native_finalize(JNIEnv *env, jobject thiz) { + LOGV("android_media_AudioTrack_native_finalize jobject: %x\n", (int)thiz); + + // delete the AudioTrack object + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + if (lpTrack) { + LOGV("deleting lpTrack: %x\n", (int)lpTrack); + lpTrack->stop(); + delete lpTrack; + } + + // delete the JNI data + AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetIntField( + thiz, javaAudioTrackFields.jniData); + if (pJniStorage) { + LOGV("deleting pJniStorage: %x\n", (int)pJniStorage); + delete pJniStorage; + } +} + +// ---------------------------------------------------------------------------- +static void android_media_AudioTrack_native_release(JNIEnv *env, jobject thiz) { + + // do everything a call to finalize would + android_media_AudioTrack_native_finalize(env, thiz); + // + reset the native resources in the Java object so any attempt to access + // them after a call to release fails. + env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0); + env->SetIntField(thiz, javaAudioTrackFields.jniData, 0); +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_native_write(JNIEnv *env, jobject thiz, + jbyteArray javaAudioData, + jint offsetInBytes, jint sizeInBytes) { + jbyte* cAudioData = NULL; + AudioTrack *lpTrack = NULL; + //LOGV("android_media_AudioTrack_native_write(offset=%d, sizeInBytes=%d) called", + // offsetInBytes, sizeInBytes); + + // get the audio track to load with samples + lpTrack = (AudioTrack *)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); + if (lpTrack == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for write()"); + } + + // get the pointer for the audio data from the java array + if (javaAudioData) { + cAudioData = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL); + if (cAudioData == NULL) { + LOGE("Error retrieving source of audio data to play, can't play"); + return 0; // out of memory or no data to load + } + } else { + LOGE("NULL java array of audio data to play, can't play"); + return 0; + } + + // give the data to the native AudioTrack object (the data starts at the offset) + ssize_t written = 0; + // regular write() or copy the data to the AudioTrack's shared memory? + if (lpTrack->sharedBuffer() == 0) { + written = lpTrack->write(cAudioData + offsetInBytes, sizeInBytes); + } else { + memcpy(lpTrack->sharedBuffer()->pointer(), cAudioData + offsetInBytes, sizeInBytes); + written = sizeInBytes; + } + + env->ReleasePrimitiveArrayCritical(javaAudioData, cAudioData, 0); + + //LOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d", + // (int)written, (int)(sizeInBytes), (int)offsetInBytes); + return (int)written; +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_native_write_short(JNIEnv *env, jobject thiz, + jshortArray javaAudioData, + jint offsetInShorts, jint sizeInShorts) { + return (android_media_AudioTrack_native_write(env, thiz, + (jbyteArray) javaAudioData, + offsetInShorts*2, sizeInShorts*2) + / 2); +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env, jobject thiz) { + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + + if (lpTrack) { + return lpTrack->frameCount(); + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for frameCount()"); + return AUDIOTRACK_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static void android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz, + jint sampleRateInHz) { + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + + if (lpTrack) { + lpTrack->setSampleRate(sampleRateInHz); + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for setSampleRate()"); + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) { + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + + if (lpTrack) { + return (jint) lpTrack->getSampleRate(); + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for getSampleRate()"); + return AUDIOTRACK_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz, + jint markerPos) { + + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + + if (lpTrack) { + return android_media_translateErrorCode( lpTrack->setMarkerPosition(markerPos) ); + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for setMarkerPosition()"); + return AUDIOTRACK_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) { + + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + uint32_t markerPos = 0; + + if (lpTrack) { + lpTrack->getMarkerPosition(&markerPos); + return (jint)markerPos; + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for getMarkerPosition()"); + return AUDIOTRACK_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz, + jint period) { + + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + + if (lpTrack) { + return android_media_translateErrorCode( lpTrack->setPositionUpdatePeriod(period) ); + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()"); + return AUDIOTRACK_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) { + + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + uint32_t period = 0; + + if (lpTrack) { + lpTrack->getPositionUpdatePeriod(&period); + return (jint)period; + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()"); + return AUDIOTRACK_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz, + jint position) { + + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + + if (lpTrack) { + return android_media_translateErrorCode( lpTrack->setPosition(position) ); + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for setPosition()"); + return AUDIOTRACK_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) { + + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + uint32_t position = 0; + + if (lpTrack) { + lpTrack->getPosition(&position); + return (jint)position; + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for getPosition()"); + return AUDIOTRACK_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz, + jint loopStart, jint loopEnd, jint loopCount) { + + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + if (lpTrack) { + return android_media_translateErrorCode( lpTrack->setLoop(loopStart, loopEnd, loopCount) ); + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for setLoop()"); + return AUDIOTRACK_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) { + + AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( + thiz, javaAudioTrackFields.nativeTrackInJavaObj); + if (lpTrack) { + return android_media_translateErrorCode( lpTrack->reload() ); + } else { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for reload()"); + return AUDIOTRACK_ERROR; + } +} + + +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz) { + int afSamplingRate; + if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) { + return DEFAULT_OUTPUT_SAMPLE_RATE; + } else { + return afSamplingRate; + } +} + + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +static JNINativeMethod gMethods[] = { + // name, signature, funcPtr + {"native_start", "()V", (void *)android_media_AudioTrack_start}, + {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, + {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, + {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, + {"native_setup", "(Ljava/lang/Object;IIIIII)I", + (void *)android_media_AudioTrack_native_setup}, + {"native_finalize", "()V", (void *)android_media_AudioTrack_native_finalize}, + {"native_release", "()V", (void *)android_media_AudioTrack_native_release}, + {"native_write_byte", "([BII)I", (void *)android_media_AudioTrack_native_write}, + {"native_write_short", "([SII)I", (void *)android_media_AudioTrack_native_write_short}, + {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, + {"native_get_native_frame_count", + "()I", (void *)android_media_AudioTrack_get_native_frame_count}, + {"native_set_playback_rate", + "(I)V", (void *)android_media_AudioTrack_set_playback_rate}, + {"native_get_playback_rate", + "()I", (void *)android_media_AudioTrack_get_playback_rate}, + {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos}, + {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos}, + {"native_set_pos_update_period", + "(I)I", (void *)android_media_AudioTrack_set_pos_update_period}, + {"native_get_pos_update_period", + "()I", (void *)android_media_AudioTrack_get_pos_update_period}, + {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position}, + {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position}, + {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop}, + {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, + {"native_get_output_sample_rate", + "()I", (void *)android_media_AudioTrack_get_output_sample_rate}, +}; + + +// field names found in android/media/AudioTrack.java +#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" +#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT" +#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" +#define JAVA_CONST_BUFFER_COUNT_NAME "BUFFER_COUNT" +#define JAVA_CONST_STREAM_VOICE_CALL_NAME "STREAM_VOICE_CALL" +#define JAVA_CONST_STREAM_SYSTEM_NAME "STREAM_SYSTEM" +#define JAVA_CONST_STREAM_RING_NAME "STREAM_RING" +#define JAVA_CONST_STREAM_MUSIC_NAME "STREAM_MUSIC" +#define JAVA_CONST_STREAM_ALARM_NAME "STREAM_ALARM" +#define JAVA_CONST_MODE_STREAM_NAME "MODE_STREAM" +#define JAVA_CONST_MODE_STATIC_NAME "MODE_STATIC" +#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" +#define JAVA_JNIDATA_FIELD_NAME "mJniData" + +#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat" +#define JAVA_AUDIOMANAGER_CLASS_NAME "android/media/AudioManager" + +// ---------------------------------------------------------------------------- +// preconditions: +// theClass is valid +bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className, + const char* constName, int* constVal) { + jfieldID javaConst = NULL; + javaConst = pEnv->GetStaticFieldID(theClass, constName, "I"); + if (javaConst != NULL) { + *constVal = pEnv->GetStaticIntField(theClass, javaConst); + return true; + } else { + LOGE("Can't find %s.%s", className, constName); + return false; + } +} + + +// ---------------------------------------------------------------------------- +int register_android_media_AudioTrack(JNIEnv *env) +{ + javaAudioTrackFields.audioTrackClass = NULL; + javaAudioTrackFields.nativeTrackInJavaObj = NULL; + javaAudioTrackFields.postNativeEventInJava = NULL; + + // Get the AudioTrack class + javaAudioTrackFields.audioTrackClass = env->FindClass(kClassPathName); + if (javaAudioTrackFields.audioTrackClass == NULL) { + LOGE("Can't find %s", kClassPathName); + return -1; + } + + // Get the postEvent method + javaAudioTrackFields.postNativeEventInJava = env->GetStaticMethodID( + javaAudioTrackFields.audioTrackClass, + JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V"); + if (javaAudioTrackFields.postNativeEventInJava == NULL) { + LOGE("Can't find AudioTrack.%s", JAVA_POSTEVENT_CALLBACK_NAME); + return -1; + } + + // Get the variables fields + // nativeTrackInJavaObj + javaAudioTrackFields.nativeTrackInJavaObj = env->GetFieldID( + javaAudioTrackFields.audioTrackClass, + JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "I"); + if (javaAudioTrackFields.nativeTrackInJavaObj == NULL) { + LOGE("Can't find AudioTrack.%s", JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME); + return -1; + } + // jniData; + javaAudioTrackFields.jniData = env->GetFieldID( + javaAudioTrackFields.audioTrackClass, + JAVA_JNIDATA_FIELD_NAME, "I"); + if (javaAudioTrackFields.jniData == NULL) { + LOGE("Can't find AudioTrack.%s", JAVA_JNIDATA_FIELD_NAME); + return -1; + } + + // Get the memory mode constants + if ( !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass, + kClassPathName, + JAVA_CONST_MODE_STATIC_NAME, &(javaAudioTrackFields.MODE_STATIC)) + || !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass, + kClassPathName, + JAVA_CONST_MODE_STREAM_NAME, &(javaAudioTrackFields.MODE_STREAM)) ) { + // error log performed in android_media_getIntConstantFromClass() + return -1; + } + + // Get the format constants from the AudioFormat class + jclass audioFormatClass = NULL; + audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME); + if (audioFormatClass == NULL) { + LOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME); + return -1; + } + if ( !android_media_getIntConstantFromClass(env, audioFormatClass, + JAVA_AUDIOFORMAT_CLASS_NAME, + JAVA_CONST_PCM16_NAME, &(javaAudioTrackFields.PCM16)) + || !android_media_getIntConstantFromClass(env, audioFormatClass, + JAVA_AUDIOFORMAT_CLASS_NAME, + JAVA_CONST_PCM8_NAME, &(javaAudioTrackFields.PCM8)) ) { + // error log performed in android_media_getIntConstantFromClass() + return -1; + } + + // Get the stream types from the AudioManager class + jclass audioManagerClass = NULL; + audioManagerClass = env->FindClass(JAVA_AUDIOMANAGER_CLASS_NAME); + if (audioManagerClass == NULL) { + LOGE("Can't find %s", JAVA_AUDIOMANAGER_CLASS_NAME); + return -1; + } + if ( !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_CONST_STREAM_VOICE_CALL_NAME, &(javaAudioTrackFields.STREAM_VOICE_CALL)) + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_CONST_STREAM_MUSIC_NAME, &(javaAudioTrackFields.STREAM_MUSIC)) + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_CONST_STREAM_SYSTEM_NAME, &(javaAudioTrackFields.STREAM_SYSTEM)) + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_CONST_STREAM_RING_NAME, &(javaAudioTrackFields.STREAM_RING)) + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_CONST_STREAM_ALARM_NAME, &(javaAudioTrackFields.STREAM_ALARM)) ) { + // error log performed in android_media_getIntConstantFromClass() + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); +} + + +// ---------------------------------------------------------------------------- diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp index 73c1283..a4388de 100644 --- a/core/jni/android_media_ToneGenerator.cpp +++ b/core/jni/android_media_ToneGenerator.cpp @@ -87,7 +87,7 @@ static void android_media_ToneGenerator_native_setup(JNIEnv *env, jobject thiz, if (lpToneGen == NULL) { LOGE("ToneGenerator creation failed \n"); - jniThrowException(env, "java/lang/OutOfMemoryException", NULL); + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); return; } LOGV("ToneGenerator lpToneGen: %x\n", (unsigned int)lpToneGen); diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp index abd0961..f14b9fa 100644 --- a/core/jni/android_net_LocalSocketImpl.cpp +++ b/core/jni/android_net_LocalSocketImpl.cpp @@ -700,7 +700,7 @@ static jint socket_readba (JNIEnv *env, jobject object, } if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) { - jniThrowException(env, "java/lang/ArrayIndexOutOfBounds", NULL); + jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); return (jint)-1; } @@ -767,7 +767,7 @@ static void socket_writeba (JNIEnv *env, jobject object, } if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) { - jniThrowException(env, "java/lang/ArrayIndexOutOfBounds", NULL); + jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); return; } diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 417ce54..8383deb 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -41,6 +41,7 @@ int dhcp_do_request(const char *ifname, in_addr_t *server, uint32_t *lease); int dhcp_stop(const char *ifname); +int dhcp_release_lease(const char *ifname); char *dhcp_get_errmsg(); } @@ -157,7 +158,7 @@ static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring if return (jboolean)(result == 0); } -static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info) +static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname) { int result; @@ -167,6 +168,16 @@ static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring i return (jboolean)(result == 0); } +static jboolean android_net_utils_releaseDhcpLease(JNIEnv* env, jobject clazz, jstring ifname) +{ + int result; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::dhcp_release_lease(nameStr); + env->ReleaseStringUTFChars(ifname, nameStr); + return (jboolean)(result == 0); +} + static jstring android_net_utils_getDhcpError(JNIEnv* env, jobject clazz) { return env->NewStringUTF(::dhcp_get_errmsg()); @@ -207,6 +218,7 @@ static JNINativeMethod gNetworkUtilMethods[] = { { "resetConnections", "(Ljava/lang/String;)I", (void *)android_net_utils_resetConnections }, { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z", (void *)android_net_utils_runDhcp }, { "stopDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_stopDhcp }, + { "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease }, { "configureNative", "(Ljava/lang/String;IIIII)Z", (void *)android_net_utils_configureInterface }, { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError }, }; diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index 48af99e..722b5b8 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -343,6 +343,32 @@ static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject clazz, return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); } +static jboolean android_net_wifi_setNumAllowedChannelsCommand(JNIEnv* env, jobject clazz, jint numChannels) +{ + char cmdstr[256]; + + int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER SCAN-CHANNELS %u", numChannels); + int cmdTooLong = numWritten >= (int)sizeof(cmdstr); + + return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); +} + +static jint android_net_wifi_getNumAllowedChannelsCommand(JNIEnv* env, jobject clazz) +{ + char reply[256]; + int numChannels; + + if (doCommand("DRIVER SCAN-CHANNELS", reply, sizeof(reply)) != 0) { + return -1; + } + // reply comes back in the form "Scan-Channels = X" where X is the + // number of channels + if (sscanf(reply, "%*s = %u", &numChannels) == 1) + return numChannels; + else + return -1; +} + static jboolean android_net_wifi_setBluetoothCoexistenceModeCommand(JNIEnv* env, jobject clazz, jint mode) { char cmdstr[256]; @@ -382,7 +408,7 @@ static jboolean android_net_wifi_addToBlacklistCommand(JNIEnv* env, jobject claz const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy); - int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "BLACKLIST %s", bssidStr) >= sizeof(cmdstr); + int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "BLACKLIST %s", bssidStr) >= (int)sizeof(cmdstr); env->ReleaseStringUTFChars(bssid, bssidStr); @@ -453,6 +479,8 @@ static JNINativeMethod gWifiMethods[] = { { "startDriverCommand", "()Z", (void*) android_net_wifi_startDriverCommand }, { "stopDriverCommand", "()Z", (void*) android_net_wifi_stopDriverCommand }, { "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand }, + { "setNumAllowedChannelsCommand", "(I)Z", (void*) android_net_wifi_setNumAllowedChannelsCommand }, + { "getNumAllowedChannelsCommand", "()I", (void*) android_net_wifi_getNumAllowedChannelsCommand }, { "setBluetoothCoexistenceModeCommand", "(I)Z", (void*) android_net_wifi_setBluetoothCoexistenceModeCommand }, { "getRssiCommand", "()I", (void*) android_net_wifi_getRssiCommand }, diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp new file mode 100644 index 0000000..19ed39f --- /dev/null +++ b/core/jni/android_server_BluetoothA2dpService.cpp @@ -0,0 +1,417 @@ +/* +** Copyright 2008, 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 "BluetoothA2dpService.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 <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#ifdef HAVE_BLUETOOTH +#include <dbus/dbus.h> +#endif + +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; + +typedef struct { + JNIEnv *env; + DBusConnection *conn; + jobject me; // for callbacks to java +} native_data_t; + +static native_data_t *nat = NULL; // global native data + +extern event_loop_native_data_t *event_loop_nat; // for the event loop JNIEnv +#endif + +#ifdef HAVE_BLUETOOTH +static void onConnectSinkResult(DBusMessage *msg, void *user); +static void onDisconnectSinkResult(DBusMessage *msg, void *user); +#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 initNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + nat = (native_data_t *)calloc(1, sizeof(native_data_t)); + if (NULL == nat) { + LOGE("%s: out of memory!", __FUNCTION__); + return false; + } + nat->env = env; + nat->me = env->NewGlobalRef(object); + + 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; + } +#endif /*HAVE_BLUETOOTH*/ + return true; +} + +static void cleanupNative(JNIEnv* env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + if (nat) { + dbus_connection_close(nat->conn); + env->DeleteGlobalRef(nat->me); + free(nat); + nat = NULL; + } +#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) { +#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; +} + +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); + env->ReleaseStringUTFChars(path, c_path); + return reply ? dbus_returns_string(env, reply) : NULL; + } +#endif + return NULL; +} + +static jstring getAddressNative(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); + env->ReleaseStringUTFChars(path, c_path); + return reply ? dbus_returns_string(env, reply) : NULL; + } +#endif + return NULL; +} + +static jboolean connectSinkNative(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, c_path, + "org.bluez.audio.Sink", "Connect", + DBUS_TYPE_INVALID); + + env->ReleaseStringUTFChars(path, c_path); + if (!ret) { + free(c_path_copy); + return JNI_FALSE; + } + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +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, + onDisconnectSinkResult, (void *)c_path_copy, c_path, + "org.bluez.audio.Sink", "Disconnect", + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(path, c_path); + if (!ret) { + free(c_path_copy); + return JNI_FALSE; + } + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jboolean isSinkConnectedNative(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", + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(path, c_path); + return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +#ifdef HAVE_BLUETOOTH +static void onConnectSinkResult(DBusMessage *msg, void *user) { + LOGV(__FUNCTION__); + + char *c_path = (char *)user; + DBusError err; + dbus_error_init(&err); + JNIEnv *env = event_loop_nat->env; + + 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) { + LOGV(__FUNCTION__); + + char *c_path = (char *)user; + DBusError err; + dbus_error_init(&err); + JNIEnv *env = event_loop_nat->env; + + 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); + // Assume it is still connected + env->CallVoidMethod(nat->me, + method_onSinkConnected, + 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); +} + +DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) { + DBusError err; + + if (!nat) { + LOGV("... skipping %s\n", __FUNCTION__); + LOGV("... ignored\n"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + dbus_error_init(&err); + + if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + 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")) { + const char *c_path = dbus_message_get_path(msg); + LOGV("... path = %s", c_path); + env->CallVoidMethod(nat->me, + method_onSinkConnected, + env->NewStringUTF(c_path)); + 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) { + LOGV("... ignored"); + } + if (env->ExceptionCheck()) { + LOGE("VM Exception occurred while handling %s.%s (%s) in %s," + " leaving for VM", + dbus_message_get_interface(msg), dbus_message_get_member(msg), + dbus_message_get_path(msg), __FUNCTION__); + } + + return result; +} +#endif + + +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}, +}; + +int register_android_server_BluetoothA2dpService(JNIEnv *env) { + jclass clazz = env->FindClass("android/server/BluetoothA2dpService"); + if (clazz == NULL) { + LOGE("Can't find android/server/BluetoothA2dpService"); + return -1; + } + +#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"); +#endif + + return AndroidRuntime::registerNativeMethods(env, + "android/server/BluetoothA2dpService", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 395a45c..1aef138 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -39,6 +39,7 @@ namespace android { static jfieldID field_mNativeData; static jmethodID method_onModeChanged; +static jmethodID method_onNameChanged; static jmethodID method_onDiscoveryStarted; static jmethodID method_onDiscoveryCompleted; static jmethodID method_onRemoteDeviceFound; @@ -60,17 +61,10 @@ static jmethodID method_onGetRemoteServiceChannelResult; static jmethodID method_onPasskeyAgentRequest; static jmethodID method_onPasskeyAgentCancel; -struct native_data_t { - DBusConnection *conn; - /* These variables are set in waitForAndDispatchEventNative() and are - valid only within the scope of this function. At any other time, they - are NULL. */ - jobject me; - JNIEnv *env; -}; +typedef event_loop_native_data_t native_data_t; // Only valid during waitForAndDispatchEventNative() -static native_data_t *event_loop_nat; +native_data_t *event_loop_nat; static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { return (native_data_t *)(env->GetIntField(object, @@ -83,6 +77,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) { #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"); @@ -142,8 +137,6 @@ static void cleanupNativeDataNative(JNIEnv* env, jobject object) { } #ifdef HAVE_BLUETOOTH -static jboolean add_adapter_event_match(JNIEnv *env, native_data_t *nat); -static void remove_adapter_event_match(JNIEnv *env, native_data_t *nat); static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, void *data); static DBusHandlerResult passkey_agent_event_filter(DBusConnection *conn, @@ -160,15 +153,46 @@ static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { LOGV(__FUNCTION__); dbus_threads_init_default(); native_data_t *nat = get_native_data(env, object); + DBusError err; + dbus_error_init(&err); + if (nat != NULL && nat->conn != NULL) { + // Add a filter for all incoming messages if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){ return JNI_FALSE; } - if (add_adapter_event_match(env, nat) != JNI_TRUE) { + // Set which messages will be processed by this dbus connection + dbus_bus_add_match(nat->conn, + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'", + &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.Manager'", + &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'", + &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); return JNI_FALSE; } + // Add an object handler for passkey agent method calls const char *path = "/android/bluetooth/PasskeyAgent"; if (!dbus_connection_register_object_path(nat->conn, path, &passkey_agent_vtable, NULL)) { @@ -177,9 +201,6 @@ static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { return JNI_FALSE; } - DBusError err; - dbus_error_init(&err); - // RegisterDefaultPasskeyAgent() will fail until hcid is up, so keep // trying for 10 seconds. int attempt; @@ -219,6 +240,9 @@ static void tearDownEventLoopNative(JNIEnv *env, jobject object) { native_data_t *nat = get_native_data(env, object); if (nat != NULL && nat->conn != NULL) { + DBusError err; + dbus_error_init(&err); + const char *path = "/android/bluetooth/PasskeyAgent"; DBusMessage *reply = dbus_func_args(env, nat->conn, BLUEZ_DBUS_BASE_PATH, @@ -227,50 +251,40 @@ static void tearDownEventLoopNative(JNIEnv *env, jobject object) { DBUS_TYPE_INVALID); if (reply) dbus_message_unref(reply); - DBusError err; - dbus_error_init(&err); - (void)dbus_connection_remove_filter(nat->conn, - event_filter, - nat); - dbus_connection_unregister_object_path(nat->conn, path); - remove_adapter_event_match(env, nat); + dbus_bus_remove_match(nat->conn, + "type='signal',interface='org.bluez.audio.Sink'", + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } + dbus_bus_remove_match(nat->conn, + "type='signal',interface='org.bluez.audio.Device'", + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } + dbus_bus_remove_match(nat->conn, + "type='signal',interface='org.bluez.audio.Manager'", + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } + dbus_bus_remove_match(nat->conn, + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'", + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } + + dbus_connection_remove_filter(nat->conn, event_filter, nat); } #endif } #ifdef HAVE_BLUETOOTH -static const char *const adapter_event_match = - "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'"; - -static jboolean add_adapter_event_match(JNIEnv *env, native_data_t *nat) { - if (nat == NULL || nat->conn == NULL) { - LOGE("%s: Not connected to d-bus!", __FUNCTION__); - return JNI_FALSE; - } - DBusError err; - dbus_error_init(&err); - dbus_bus_add_match(nat->conn, adapter_event_match, &err); - if (dbus_error_is_set(&err)) { - LOG_AND_FREE_DBUS_ERROR(&err); - return JNI_FALSE; - } - return JNI_TRUE; -} - -static void remove_adapter_event_match(JNIEnv *env, native_data_t *nat) { - if (nat->conn == NULL) { - LOGE("%s: Not connected to d-bus!", __FUNCTION__); - return; - } - DBusError err; - dbus_error_init(&err); - dbus_bus_remove_match(nat->conn, adapter_event_match, &err); - if (dbus_error_is_set(&err)) { - LOG_AND_FREE_DBUS_ERROR(&err); - } -} +extern DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env); // Called by dbus during WaitForAndDispatchEventNative() static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, @@ -288,8 +302,9 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - LOGV("%s: Received signal %s:%s", __FUNCTION__, - dbus_message_get_interface(msg), dbus_message_get_member(msg)); + LOGV("%s: Received signal %s:%s from %s", __FUNCTION__, + dbus_message_get_interface(msg), dbus_message_get_member(msg), + dbus_message_get_path(msg)); if (dbus_message_is_signal(msg, "org.bluez.Adapter", @@ -471,11 +486,35 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; - } else { - LOGV("... ignored"); + } 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); + env->CallVoidMethod(nat->me, + method_onModeChanged, + env->NewStringUTF(c_mode)); + } 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", + "NameChanged")) { + char *c_name; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_name, + DBUS_TYPE_INVALID)) { + LOGV("... name = %s", c_name); + env->CallVoidMethod(nat->me, + method_onNameChanged, + env->NewStringUTF(c_name)); + } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return a2dp_event_filter(msg, env); } // Called by dbus during WaitForAndDispatchEventNative() @@ -557,9 +596,9 @@ static jboolean waitForAndDispatchEventNative(JNIEnv *env, jobject object, nat->me = object; nat->env = env; event_loop_nat = nat; - ret = dbus_connection_read_write_dispatch(nat->conn, - timeout_ms) == TRUE ? - JNI_TRUE : JNI_FALSE; + ret = dbus_connection_read_write_dispatch_greedy(nat->conn, + timeout_ms) == TRUE ? + JNI_TRUE : JNI_FALSE; event_loop_nat = NULL; nat->me = NULL; nat->env = NULL; @@ -604,7 +643,7 @@ void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user) { JNIEnv *env = event_loop_nat->env; jint channel = -2; - LOGV("... address = %s", context->address); + LOGV("... address = %s", address); if (dbus_set_error_from_message(&err, msg) || !dbus_message_get_args(msg, &err, diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp index 97daed3..450cee2 100644 --- a/core/jni/android_text_AndroidCharacter.cpp +++ b/core/jni/android_text_AndroidCharacter.cpp @@ -43,7 +43,7 @@ static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray, j } if (env->GetArrayLength(srcArray) < count || env->GetArrayLength(destArray) < count) { - jniThrowException(env, "java/lang/ArrayIndexException", NULL); + jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); goto DIRECTION_END; } @@ -81,7 +81,7 @@ static jboolean mirror(JNIEnv* env, jobject obj, jcharArray charArray, int start } if (start > start + count || env->GetArrayLength(charArray) < count) { - jniThrowException(env, "java/lang/ArrayIndexException", NULL); + jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); goto MIRROR_END; } diff --git a/core/jni/android_pim_Time.cpp b/core/jni/android_text_format_Time.cpp index c1dd499..8e41ec7 100644 --- a/core/jni/android_pim_Time.cpp +++ b/core/jni/android_text_format_Time.cpp @@ -25,6 +25,7 @@ #include "android_runtime/AndroidRuntime.h" #include <utils/TimeUtils.h> #include <nativehelper/JNIHelp.h> +#include <cutils/tztime.h> namespace android { @@ -41,6 +42,17 @@ static jfieldID g_isdstField = 0; static jfieldID g_gmtoffField = 0; static jfieldID g_timezoneField = 0; +static jfieldID g_shortMonthsField = 0; +static jfieldID g_longMonthsField = 0; +static jfieldID g_shortWeekdaysField = 0; +static jfieldID g_longWeekdaysField = 0; +static jfieldID g_timeOnlyFormatField = 0; +static jfieldID g_dateOnlyFormatField = 0; +static jfieldID g_dateTimeFormatField = 0; +static jfieldID g_amField = 0; +static jfieldID g_pmField = 0; +static jfieldID g_dateCommandField = 0; + static inline bool java2time(JNIEnv* env, Time* t, jobject o) { t->t.tm_sec = env->GetIntField(o, g_secField); @@ -89,7 +101,7 @@ static inline void time2java(JNIEnv* env, jobject o, const Time &t) // ============================================================================ -static jlong android_pim_Time_normalize(JNIEnv* env, jobject This, +static jlong android_text_format_Time_normalize(JNIEnv* env, jobject This, jboolean ignoreDst) { Time t; @@ -104,7 +116,7 @@ static jlong android_pim_Time_normalize(JNIEnv* env, jobject This, return result; } -static void android_pim_Time_switchTimezone(JNIEnv* env, jobject This, +static void android_text_format_Time_switchTimezone(JNIEnv* env, jobject This, jstring timezoneObject) { Time t; @@ -123,7 +135,7 @@ static void android_pim_Time_switchTimezone(JNIEnv* env, jobject This, env->SetObjectField(This, g_timezoneField, timezoneObject); } -static jint android_pim_Time_compare(JNIEnv* env, jobject clazz, +static jint android_text_format_Time_compare(JNIEnv* env, jobject clazz, jobject aObject, jobject bObject) { Time a, b; @@ -142,7 +154,7 @@ static jint android_pim_Time_compare(JNIEnv* env, jobject clazz, return result; } -static jstring android_pim_Time_format2445(JNIEnv* env, jobject This) +static jstring android_text_format_Time_format2445(JNIEnv* env, jobject This) { Time t; if (!java2time(env, &t, This)) return env->NewStringUTF(""); @@ -168,25 +180,91 @@ static jstring android_pim_Time_format2445(JNIEnv* env, jobject This) } } -static jstring android_pim_Time_format(JNIEnv* env, jobject This, +static jstring android_text_format_Time_format(JNIEnv* env, jobject This, jstring formatObject) { Time t; + struct strftime_locale locale; + jclass timeClass = env->FindClass("android/text/format/Time"); + jstring js_mon[12], js_month[12], js_wday[7], js_weekday[7]; + jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt; + jobjectArray ja; + if (!java2time(env, &t, This)) return env->NewStringUTF(""); + + ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortMonthsField); + for (int i = 0; i < 12; i++) { + js_mon[i] = (jstring) env->GetObjectArrayElement(ja, i); + locale.mon[i] = env->GetStringUTFChars(js_mon[i], NULL); + } + + ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longMonthsField); + for (int i = 0; i < 12; i++) { + js_month[i] = (jstring) env->GetObjectArrayElement(ja, i); + locale.month[i] = env->GetStringUTFChars(js_month[i], NULL); + } + + ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField); + for (int i = 0; i < 7; i++) { + js_wday[i] = (jstring) env->GetObjectArrayElement(ja, i); + locale.wday[i] = env->GetStringUTFChars(js_wday[i], NULL); + } + + ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longWeekdaysField); + for (int i = 0; i < 7; i++) { + js_weekday[i] = (jstring) env->GetObjectArrayElement(ja, i); + locale.weekday[i] = env->GetStringUTFChars(js_weekday[i], NULL); + } + + js_X_fmt = (jstring) env->GetStaticObjectField(timeClass, g_timeOnlyFormatField); + locale.X_fmt = env->GetStringUTFChars(js_X_fmt, NULL); + + js_x_fmt = (jstring) env->GetStaticObjectField(timeClass, g_dateOnlyFormatField); + locale.x_fmt = env->GetStringUTFChars(js_x_fmt, NULL); + + js_c_fmt = (jstring) env->GetStaticObjectField(timeClass, g_dateTimeFormatField); + locale.c_fmt = env->GetStringUTFChars(js_c_fmt, NULL); + + js_am = (jstring) env->GetStaticObjectField(timeClass, g_amField); + locale.am = env->GetStringUTFChars(js_am, NULL); + + js_pm = (jstring) env->GetStaticObjectField(timeClass, g_pmField); + locale.pm = env->GetStringUTFChars(js_pm, NULL); + + js_date_fmt = (jstring) env->GetStaticObjectField(timeClass, g_dateCommandField); + locale.date_fmt = env->GetStringUTFChars(js_date_fmt, NULL); + ACQUIRE_TIMEZONE(This, t) const char* format = env->GetStringUTFChars(formatObject, NULL); - String8 r = t.format(format); + String8 r = t.format(format, &locale); env->ReleaseStringUTFChars(formatObject, format); RELEASE_TIMEZONE(This, t) + for (int i = 0; i < 12; i++) { + env->ReleaseStringUTFChars(js_mon[i], locale.mon[i]); + env->ReleaseStringUTFChars(js_month[i], locale.month[i]); + } + + for (int i = 0; i < 7; i++) { + env->ReleaseStringUTFChars(js_wday[i], locale.wday[i]); + env->ReleaseStringUTFChars(js_weekday[i], locale.weekday[i]); + } + + env->ReleaseStringUTFChars(js_X_fmt, locale.X_fmt); + env->ReleaseStringUTFChars(js_x_fmt, locale.x_fmt); + env->ReleaseStringUTFChars(js_c_fmt, locale.c_fmt); + env->ReleaseStringUTFChars(js_am, locale.am); + env->ReleaseStringUTFChars(js_pm, locale.pm); + env->ReleaseStringUTFChars(js_date_fmt, locale.date_fmt); + return env->NewStringUTF(r.string()); } -static jstring android_pim_Time_toString(JNIEnv* env, jobject This) +static jstring android_text_format_Time_toString(JNIEnv* env, jobject This) { Time t; if (!java2time(env, &t, This)) return env->NewStringUTF("");; @@ -199,7 +277,7 @@ static jstring android_pim_Time_toString(JNIEnv* env, jobject This) return env->NewStringUTF(r.string()); } -static void android_pim_Time_setToNow(JNIEnv* env, jobject This) +static void android_text_format_Time_setToNow(JNIEnv* env, jobject This) { env->SetBooleanField(This, g_allDayField, JNI_FALSE); Time t; @@ -211,7 +289,7 @@ static void android_pim_Time_setToNow(JNIEnv* env, jobject This) RELEASE_TIMEZONE(This, t) } -static jlong android_pim_Time_toMillis(JNIEnv* env, jobject This, +static jlong android_text_format_Time_toMillis(JNIEnv* env, jobject This, jboolean ignoreDst) { Time t; @@ -225,7 +303,7 @@ static jlong android_pim_Time_toMillis(JNIEnv* env, jobject This, return result; } -static void android_pim_Time_set(JNIEnv* env, jobject This, jlong millis) +static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis) { env->SetBooleanField(This, g_allDayField, JNI_FALSE); Time t; @@ -271,48 +349,71 @@ static bool check_char(JNIEnv* env, const jchar *s, int spos, jchar expected) } -static void android_pim_Time_parse(JNIEnv* env, jobject This, jstring strObj) +static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj) { jsize len = env->GetStringLength(strObj); const jchar *s = env->GetStringChars(strObj, NULL); bool thrown = false; int n; + jboolean inUtc = false; + if (len < 8) { + char msg[100]; + sprintf(msg, "String too short -- expected at least 8 characters."); + jniThrowException(env, "android/util/TimeFormatException", msg); + return false; + } + + // year n = get_char(env, s, 0, 1000, &thrown); n += get_char(env, s, 1, 100, &thrown); n += get_char(env, s, 2, 10, &thrown); n += get_char(env, s, 3, 1, &thrown); - if (thrown) return; + if (thrown) return false; env->SetIntField(This, g_yearField, n); + // month n = get_char(env, s, 4, 10, &thrown); n += get_char(env, s, 5, 1, &thrown); n--; - if (thrown) return; + if (thrown) return false; env->SetIntField(This, g_monField, n); + // day of month n = get_char(env, s, 6, 10, &thrown); n += get_char(env, s, 7, 1, &thrown); - if (thrown) return; + if (thrown) return false; env->SetIntField(This, g_mdayField, n); - if (len >= 15) { + if (len > 8) { + // T + if (!check_char(env, s, 8, 'T')) return false; env->SetBooleanField(This, g_allDayField, JNI_FALSE); + + // hour n = get_char(env, s, 9, 10, &thrown); n += get_char(env, s, 10, 1, &thrown); - if (thrown) return; + if (thrown) return false; env->SetIntField(This, g_hourField, n); + // min n = get_char(env, s, 11, 10, &thrown); n += get_char(env, s, 12, 1, &thrown); - if (thrown) return; + if (thrown) return false; env->SetIntField(This, g_minField, n); + // sec n = get_char(env, s, 13, 10, &thrown); n += get_char(env, s, 14, 1, &thrown); - if (thrown) return; + if (thrown) return false; env->SetIntField(This, g_secField, n); + + if (len > 15) { + // Z + if (!check_char(env, s, 15, 'Z')) return false; + inUtc = true; + } } else { env->SetBooleanField(This, g_allDayField, JNI_TRUE); env->SetIntField(This, g_hourField, 0); @@ -326,92 +427,10 @@ static void android_pim_Time_parse(JNIEnv* env, jobject This, jstring strObj) env->SetLongField(This, g_gmtoffField, 0); env->ReleaseStringChars(strObj, s); -} - -static jboolean android_pim_Time_parse2445(JNIEnv* env, jobject This, - jstring strObj) -{ - jsize len = env->GetStringLength(strObj); - const jchar *s = env->GetStringChars(strObj, NULL); - - bool thrown = false; - int n; - jboolean inUtc = false; - - if (len < 8) { - char msg[100]; - sprintf(msg, "String too short -- expected at least 8 characters."); - jniThrowException(env, "android/util/TimeFormatException", msg); - return false; - } - - // year - n = get_char(env, s, 0, 1000, &thrown); - n += get_char(env, s, 1, 100, &thrown); - n += get_char(env, s, 2, 10, &thrown); - n += get_char(env, s, 3, 1, &thrown); - if (thrown) return false; - env->SetIntField(This, g_yearField, n); - - // month - n = get_char(env, s, 4, 10, &thrown); - n += get_char(env, s, 5, 1, &thrown); - n--; - if (thrown) return false; - env->SetIntField(This, g_monField, n); - - // day of month - n = get_char(env, s, 6, 10, &thrown); - n += get_char(env, s, 7, 1, &thrown); - if (thrown) return false; - env->SetIntField(This, g_mdayField, n); - - if (len > 8) { - // T - if (!check_char(env, s, 8, 'T')) return false; - env->SetBooleanField(This, g_allDayField, JNI_FALSE); - - // hour - n = get_char(env, s, 9, 10, &thrown); - n += get_char(env, s, 10, 1, &thrown); - if (thrown) return false; - env->SetIntField(This, g_hourField, n); - - // min - n = get_char(env, s, 11, 10, &thrown); - n += get_char(env, s, 12, 1, &thrown); - if (thrown) return false; - env->SetIntField(This, g_minField, n); - - // sec - n = get_char(env, s, 13, 10, &thrown); - n += get_char(env, s, 14, 1, &thrown); - if (thrown) return false; - env->SetIntField(This, g_secField, n); - - if (len > 15) { - // Z - if (!check_char(env, s, 15, 'Z')) return false; - inUtc = true; - } - } else { - // all day - env->SetBooleanField(This, g_allDayField, JNI_TRUE); - env->SetIntField(This, g_hourField, 0); - env->SetIntField(This, g_minField, 0); - env->SetIntField(This, g_secField, 0); - } - - env->SetIntField(This, g_wdayField, 0); - env->SetIntField(This, g_ydayField, 0); - env->SetIntField(This, g_isdstField, -1); - env->SetLongField(This, g_gmtoffField, 0); - - env->ReleaseStringChars(strObj, s); return inUtc; } -static jboolean android_pim_Time_parse3339(JNIEnv* env, +static jboolean android_text_format_Time_parse3339(JNIEnv* env, jobject This, jstring strObj) { @@ -531,7 +550,7 @@ static jboolean android_pim_Time_parse3339(JNIEnv* env, if (offset != 0) { // we need to normalize after applying the hour and minute offsets - android_pim_Time_normalize(env, This, false /* use isdst */); + android_text_format_Time_normalize(env, This, false /* use isdst */); // The timezone is set to UTC in the calling Java code. } } else { @@ -556,23 +575,22 @@ static jboolean android_pim_Time_parse3339(JNIEnv* env, */ static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ - { "normalize", "(Z)J", (void*)android_pim_Time_normalize }, - { "switchTimezone", "(Ljava/lang/String;)V", (void*)android_pim_Time_switchTimezone }, - { "compare", "(Landroid/pim/Time;Landroid/pim/Time;)I", (void*)android_pim_Time_compare }, - { "format", "(Ljava/lang/String;)Ljava/lang/String;", (void*)android_pim_Time_format }, - { "format2445", "()Ljava/lang/String;", (void*)android_pim_Time_format2445 }, - { "toString", "()Ljava/lang/String;", (void*)android_pim_Time_toString }, - { "parse", "(Ljava/lang/String;)V", (void*)android_pim_Time_parse }, - { "nativeParse2445", "(Ljava/lang/String;)Z", (void*)android_pim_Time_parse2445 }, - { "nativeParse3339", "(Ljava/lang/String;)Z", (void*)android_pim_Time_parse3339 }, - { "setToNow", "()V", (void*)android_pim_Time_setToNow }, - { "toMillis", "(Z)J", (void*)android_pim_Time_toMillis }, - { "set", "(J)V", (void*)android_pim_Time_set } + { "normalize", "(Z)J", (void*)android_text_format_Time_normalize }, + { "switchTimezone", "(Ljava/lang/String;)V", (void*)android_text_format_Time_switchTimezone }, + { "compare", "(Landroid/text/format/Time;Landroid/text/format/Time;)I", (void*)android_text_format_Time_compare }, + { "format1", "(Ljava/lang/String;)Ljava/lang/String;", (void*)android_text_format_Time_format }, + { "format2445", "()Ljava/lang/String;", (void*)android_text_format_Time_format2445 }, + { "toString", "()Ljava/lang/String;", (void*)android_text_format_Time_toString }, + { "nativeParse", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse }, + { "nativeParse3339", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse3339 }, + { "setToNow", "()V", (void*)android_text_format_Time_setToNow }, + { "toMillis", "(Z)J", (void*)android_text_format_Time_toMillis }, + { "set", "(J)V", (void*)android_text_format_Time_set } }; -int register_android_pim_Time(JNIEnv* env) +int register_android_text_format_Time(JNIEnv* env) { - jclass timeClass = env->FindClass("android/pim/Time"); + jclass timeClass = env->FindClass("android/text/format/Time"); g_allDayField = env->GetFieldID(timeClass, "allDay", "Z"); g_secField = env->GetFieldID(timeClass, "second", "I"); @@ -587,7 +605,18 @@ int register_android_pim_Time(JNIEnv* env) g_gmtoffField = env->GetFieldID(timeClass, "gmtoff", "J"); g_timezoneField = env->GetFieldID(timeClass, "timezone", "Ljava/lang/String;"); - return AndroidRuntime::registerNativeMethods(env, "android/pim/Time", gMethods, NELEM(gMethods)); + g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;"); + g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;"); + g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;"); + g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;"); + g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;"); + g_dateOnlyFormatField = env->GetStaticFieldID(timeClass, "sDateOnlyFormat", "Ljava/lang/String;"); + g_dateTimeFormatField = env->GetStaticFieldID(timeClass, "sDateTimeFormat", "Ljava/lang/String;"); + g_amField = env->GetStaticFieldID(timeClass, "sAm", "Ljava/lang/String;"); + g_pmField = env->GetStaticFieldID(timeClass, "sPm", "Ljava/lang/String;"); + g_dateCommandField = env->GetStaticFieldID(timeClass, "sDateCommand", "Ljava/lang/String;"); + + return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods)); } }; // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 8a62159..add1080 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -901,7 +901,8 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla return JNI_FALSE; } - jint* dest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + jint* dest = baseDest; if (dest == NULL) { env->ReleasePrimitiveArrayCritical(attrs, src, 0); doThrow(env, "java/lang/OutOfMemoryError"); @@ -1074,7 +1075,7 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla indices[0] = indicesIdx; env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); } - env->ReleasePrimitiveArrayCritical(outValues, dest, 0); + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); env->ReleasePrimitiveArrayCritical(attrs, src, 0); return JNI_TRUE; @@ -1112,7 +1113,8 @@ static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, job return JNI_FALSE; } - jint* dest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + jint* dest = baseDest; if (dest == NULL) { env->ReleasePrimitiveArrayCritical(attrs, src, 0); doThrow(env, "java/lang/OutOfMemoryError"); @@ -1201,7 +1203,7 @@ static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, job env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); } - env->ReleasePrimitiveArrayCritical(outValues, dest, 0); + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); env->ReleasePrimitiveArrayCritical(attrs, src, 0); return JNI_TRUE; @@ -1243,7 +1245,8 @@ static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject claz const jsize NV = env->GetArrayLength(outValues); - jint* dest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + jint* dest = baseDest; if (dest == NULL) { doThrow(env, "java/lang/OutOfMemoryError"); return JNI_FALSE; @@ -1295,7 +1298,7 @@ static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject claz res.unlock(); - env->ReleasePrimitiveArrayCritical(outValues, dest, 0); + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); return i; } diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 08c4f1c..3feccde 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -88,6 +88,11 @@ jint android_os_Process_myPid(JNIEnv* env, jobject clazz) return getpid(); } +jint android_os_Process_myUid(JNIEnv* env, jobject clazz) +{ + return getuid(); +} + jint android_os_Process_myTid(JNIEnv* env, jobject clazz) { #ifdef HAVE_GETTID @@ -707,6 +712,7 @@ static jlong android_os_Process_getPss(JNIEnv* env, jobject clazz, jint pid) static const JNINativeMethod methods[] = { {"myPid", "()I", (void*)android_os_Process_myPid}, {"myTid", "()I", (void*)android_os_Process_myTid}, + {"myUid", "()I", (void*)android_os_Process_myUid}, {"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName}, {"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName}, {"setThreadPriority", "(II)V", (void*)android_os_Process_setThreadPriority}, diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp index af03016..a985c24 100644 --- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp @@ -332,12 +332,8 @@ jboolean jni_eglGetConfigs(JNIEnv *_env, jobject _this, jobject display, if (success && configs) { for (int i=0 ; i<num ; i++) { - jobject obj = _env->GetObjectArrayElement(configs, i); - if (obj == NULL) { - doThrow(_env, "java/lang/NullPointerException"); - break; - } - _env->SetIntField(obj, gConfig_EGLConfigFieldID, (jint)nativeConfigs[i]); + jobject obj = _env->NewObject(gConfig_class, gConfig_ctorID, (jint)nativeConfigs[i]); + _env->SetObjectArrayElement(configs, i, obj); } } return success; @@ -396,8 +392,7 @@ jboolean jni_eglMakeCurrent(JNIEnv *_env, jobject _this, jobject display, jobjec jstring jni_eglQueryString(JNIEnv *_env, jobject _this, jobject display, jint name) { EGLDisplay dpy = getDisplay(_env, display); const char* chars = eglQueryString(dpy, name); - return _env->NewString((const jchar *)chars, - (jsize)strlen((const char *)chars)); + return _env->NewStringUTF(chars); } jboolean jni_eglSwapBuffers(JNIEnv *_env, jobject _this, jobject display, jobject surface) { diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp index 1cd23b0..9b09c9b 100644 --- a/core/jni/com_google_android_gles_jni_GLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp @@ -35,6 +35,7 @@ static jclass bufferClass; static jclass OOMEClass; static jclass UOEClass; static jclass IAEClass; +static jclass AIOOBEClass; static jmethodID getBasePointerID; static jmethodID getBaseArrayID; static jmethodID getBaseArrayOffsetID; @@ -78,10 +79,13 @@ nativeClassInit(JNIEnv *_env, jclass glImplClass) _env->FindClass("java/lang/OutOfMemoryError"); jclass UOEClassLocal = _env->FindClass("java/lang/UnsupportedOperationException"); + jclass AIOOBEClassLocal = + _env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal); OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal); UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal); + AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal); } static void * @@ -526,12 +530,18 @@ android_glDrawElements__IIILjava_nio_Buffer_2 GLvoid *indices = (GLvoid *) 0; indices = (GLvoid *)getPointer(_env, indices_buf, &_array, &_remaining); + if (_remaining < count) { + _env->ThrowNew(AIOOBEClass, "remaining() < count"); + goto exit; + } glDrawElements( (GLenum)mode, (GLsizei)count, (GLenum)type, (GLvoid *)indices ); + +exit: if (_array) { releasePointer(_env, _array, indices, JNI_FALSE); } @@ -1647,20 +1657,8 @@ exit: jstring android_glGetString (JNIEnv *_env, jobject _this, jint name) { - const GLubyte * chars = glGetString((GLenum)name); - - int len = strlen((const char *)chars); - jchar * wchars = (jchar *)malloc(len * sizeof(jchar)); - if (wchars == (jchar*) 0) { - _env->ThrowNew(OOMEClass, "No space for glGetString output"); - return (jstring) 0; - } - // Copy bytes -> chars, including trailing '\0' - for (int i = 0; i <= len; i++) { - wchars[i] = (jchar) chars[i]; - } - jstring output = _env->NewString(wchars, (jsize) len); - free(wchars); + const char * chars = (const char *)glGetString((GLenum)name); + jstring output = _env->NewStringUTF(chars); return output; } /* void glHint ( GLenum target, GLenum mode ) */ diff --git a/core/jni/server/Android.mk b/core/jni/server/Android.mk index d108330..bd08da3 100644 --- a/core/jni/server/Android.mk +++ b/core/jni/server/Android.mk @@ -21,11 +21,13 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libui +ifeq ($(TARGET_SIMULATOR),true) ifeq ($(TARGET_OS),linux) ifeq ($(TARGET_ARCH),x86) LOCAL_LDLIBS += -lpthread -ldl -lrt endif endif +endif ifeq ($(WITH_MALLOC_LEAK_CHECK),true) LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK diff --git a/core/jni/server/com_android_server_AlarmManagerService.cpp b/core/jni/server/com_android_server_AlarmManagerService.cpp index a81a0ff..0f37921 100644 --- a/core/jni/server/com_android_server_AlarmManagerService.cpp +++ b/core/jni/server/com_android_server_AlarmManagerService.cpp @@ -44,6 +44,23 @@ namespace android { +static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv* env, jobject obj, jint fd, jint minswest) +{ +#if HAVE_ANDROID_OS + struct timezone tz; + + tz.tz_minuteswest = minswest; + tz.tz_dsttime = 0; + + int result = ioctl(fd, ANDROID_ALARM_SET_TIMEZONE, &tz); + if (result < 0) { + LOGE("Unable to set kernel timezone to %d: %s\n", minswest, strerror(errno)); + return -1; + } + return 0; +#endif +} + static jint android_server_AlarmManagerService_init(JNIEnv* env, jobject obj) { #if HAVE_ANDROID_OS @@ -101,6 +118,7 @@ static JNINativeMethod sMethods[] = { {"close", "(I)V", (void*)android_server_AlarmManagerService_close}, {"set", "(IIJ)V", (void*)android_server_AlarmManagerService_set}, {"waitForAlarm", "(I)I", (void*)android_server_AlarmManagerService_waitForAlarm}, + {"setKernelTimezone", "(II)I", (void*)android_server_AlarmManagerService_setKernelTimezone}, }; int register_android_server_AlarmManagerService(JNIEnv* env) diff --git a/core/jni/server/com_android_server_SensorService.cpp b/core/jni/server/com_android_server_SensorService.cpp index 37f6231..695a8a3 100644 --- a/core/jni/server/com_android_server_SensorService.cpp +++ b/core/jni/server/com_android_server_SensorService.cpp @@ -23,7 +23,6 @@ namespace android { - static struct file_descriptor_offsets_t { jclass mClass; @@ -41,16 +40,26 @@ static struct parcel_file_descriptor_offsets_t * The method below are not thread-safe and not intended to be */ +static sensors_control_device_t* sSensorDevice = 0; + static jint android_init(JNIEnv *env, jclass clazz) { - return sensors_control_init(); + sensors_module_t* module; + if (hw_get_module(SENSORS_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) { + if (sensors_control_open(&module->common, &sSensorDevice) == 0) { + const struct sensor_t* list; + int count = module->get_sensors_list(module, &list); + return count; + } + } + return 0; } static jobject android_open(JNIEnv *env, jclass clazz) { - int fd = sensors_control_open(); + int fd = sSensorDevice->open_data_source(sSensorDevice); // new FileDescriptor() jobject filedescriptor = env->NewObject( gFileDescriptorOffsets.mClass, @@ -70,20 +79,29 @@ android_open(JNIEnv *env, jclass clazz) static jboolean android_activate(JNIEnv *env, jclass clazz, jint sensor, jboolean activate) { - uint32_t active = sensors_control_activate(activate ? sensor : 0, sensor); - return (activate && !active) ? false : true; + int active = sSensorDevice->activate(sSensorDevice, sensor, activate); + return (active<0) ? false : true; } static jint android_set_delay(JNIEnv *env, jclass clazz, jint ms) { - return sensors_control_delay(ms); + return sSensorDevice->set_delay(sSensorDevice, ms); +} + +static jint +android_data_wake(JNIEnv *env, jclass clazz) +{ + int res = sSensorDevice->wake(sSensorDevice); + return res; } + static JNINativeMethod gMethods[] = { {"_sensors_control_init", "()I", (void*) android_init }, {"_sensors_control_open", "()Landroid/os/ParcelFileDescriptor;", (void*) android_open }, {"_sensors_control_activate", "(IZ)Z", (void*) android_activate }, + {"_sensors_control_wake", "()I", (void*) android_data_wake }, {"_sensors_control_set_delay","(I)I", (void*) android_set_delay }, }; |