diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /core/jni | |
download | frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2 |
Initial Contribution
Diffstat (limited to 'core/jni')
116 files changed, 38472 insertions, 0 deletions
diff --git a/core/jni/ActivityManager.cpp b/core/jni/ActivityManager.cpp new file mode 100644 index 0000000..9017827 --- /dev/null +++ b/core/jni/ActivityManager.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <unistd.h> +#include <android_runtime/ActivityManager.h> +#include <utils/IBinder.h> +#include <utils/IServiceManager.h> +#include <utils/Parcel.h> +#include <utils/String8.h> + +namespace android { + +const uint32_t OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 4; + +// Perform ContentProvider.openFile() on the given URI, returning +// the resulting native file descriptor. Returns < 0 on error. +int openContentProviderFile(const String16& uri) +{ + int fd = -1; + + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> am = sm->getService(String16("activity")); + if (am != NULL) { + Parcel data, reply; + data.writeInterfaceToken(String16("android.app.IActivityManager")); + data.writeString16(uri); + status_t ret = am->transact(OPEN_CONTENT_URI_TRANSACTION, data, &reply); + if (ret == NO_ERROR) { + int32_t exceptionCode = reply.readInt32(); + if (!exceptionCode) { + // Success is indicated here by a nonzero int followed by the fd; + // failure by a zero int with no data following. + if (reply.readInt32() != 0) { + fd = dup(reply.readFileDescriptor()); + } + } else { + // An exception was thrown back; fall through to return failure + LOGD("openContentUri(%s) caught exception %d\n", + String8(uri).string(), exceptionCode); + } + } + } + + return fd; +} + +} /* namespace android */ diff --git a/core/jni/Android.mk b/core/jni/Android.mk new file mode 100644 index 0000000..e9009e6 --- /dev/null +++ b/core/jni/Android.mk @@ -0,0 +1,176 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_CFLAGS += -DHAVE_CONFIG_H -DKHTML_NO_EXCEPTIONS -DGKWQ_NO_JAVA +LOCAL_CFLAGS += -DNO_SUPPORT_JS_BINDING -DQT_NO_WHEELEVENT -DKHTML_NO_XBL +LOCAL_CFLAGS += -U__APPLE__ + +ifeq ($(TARGET_ARCH), arm) + LOCAL_CFLAGS += -DPACKED="__attribute__ ((packed))" +else + LOCAL_CFLAGS += -DPACKED="" +endif + +LOCAL_SRC_FILES:= \ + ActivityManager.cpp \ + AndroidRuntime.cpp \ + CursorWindow.cpp \ + com_google_android_gles_jni_EGLImpl.cpp \ + com_google_android_gles_jni_GLImpl.cpp.arm \ + android_database_CursorWindow.cpp \ + android_database_SQLiteDebug.cpp \ + android_database_SQLiteDatabase.cpp \ + android_database_SQLiteProgram.cpp \ + android_database_SQLiteQuery.cpp \ + android_database_SQLiteStatement.cpp \ + android_view_Display.cpp \ + android_view_Surface.cpp \ + android_view_ViewRoot.cpp \ + android_text_AndroidCharacter.cpp \ + android_text_KeyCharacterMap.cpp \ + android_os_Debug.cpp \ + android_os_Exec.cpp \ + android_os_FileUtils.cpp \ + android_os_MemoryFile.cpp \ + android_os_ParcelFileDescriptor.cpp \ + android_os_Power.cpp \ + android_os_StatFs.cpp \ + android_os_SystemClock.cpp \ + android_os_SystemProperties.cpp \ + android_os_UEventObserver.cpp \ + android_os_NetStat.cpp \ + android_os_Hardware.cpp \ + android_net_LocalSocketImpl.cpp \ + android_net_NetUtils.cpp \ + android_net_wifi_Wifi.cpp \ + android_nio_utils.cpp \ + android_pim_EventRecurrence.cpp \ + android_pim_Time.cpp \ + android_security_Md5MessageDigest.cpp \ + android_util_AssetManager.cpp \ + android_util_Binder.cpp \ + android_util_EventLog.cpp \ + android_util_Log.cpp \ + android_util_FloatMath.cpp \ + android_util_Process.cpp \ + android_util_StringBlock.cpp \ + android_util_XmlBlock.cpp \ + android_util_Base64.cpp \ + android/graphics/Bitmap.cpp \ + android/graphics/BitmapFactory.cpp \ + android/graphics/Camera.cpp \ + android/graphics/Canvas.cpp \ + android/graphics/ColorFilter.cpp \ + android/graphics/DrawFilter.cpp \ + android/graphics/CreateJavaOutputStreamAdaptor.cpp \ + android/graphics/Graphics.cpp \ + android/graphics/Interpolator.cpp \ + android/graphics/LayerRasterizer.cpp \ + android/graphics/MaskFilter.cpp \ + android/graphics/Matrix.cpp \ + android/graphics/Movie.cpp \ + android/graphics/NIOBuffer.cpp \ + android/graphics/NinePatch.cpp \ + android/graphics/NinePatchImpl.cpp \ + android/graphics/Paint.cpp \ + android/graphics/Path.cpp \ + android/graphics/PathMeasure.cpp \ + android/graphics/PathEffect.cpp \ + android_graphics_PixelFormat.cpp \ + android/graphics/Picture.cpp \ + android/graphics/PorterDuff.cpp \ + android/graphics/Rasterizer.cpp \ + android/graphics/Region.cpp \ + android/graphics/Shader.cpp \ + android/graphics/Typeface.cpp \ + android/graphics/Xfermode.cpp \ + android_media_AudioSystem.cpp \ + android_media_ToneGenerator.cpp \ + android_hardware_Camera.cpp \ + android_hardware_SensorManager.cpp \ + android_debug_JNITest.cpp \ + android_util_FileObserver.cpp \ + android/opengl/poly_clip.cpp.arm \ + android/opengl/util.cpp.arm \ + android_bluetooth_Database.cpp \ + android_bluetooth_HeadsetBase.cpp \ + android_bluetooth_common.cpp \ + android_bluetooth_BluetoothAudioGateway.cpp \ + android_bluetooth_RfcommSocket.cpp \ + android_bluetooth_ScoSocket.cpp \ + android_server_BluetoothDeviceService.cpp \ + android_server_BluetoothEventLoop.cpp \ + android_message_digest_sha1.cpp \ + android_ddm_DdmHandleNativeHeap.cpp \ + android_location_GpsLocationProvider.cpp \ + com_android_internal_os_ZygoteInit.cpp \ + com_android_internal_graphics_NativeUtils.cpp + +LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) \ + $(LOCAL_PATH)/android/graphics \ + $(call include-path-for, corecg graphics) \ + $(call include-path-for, libhardware)/hardware \ + $(LOCAL_PATH)/../../include/ui \ + $(LOCAL_PATH)/../../include/utils \ + external/sqlite/dist \ + external/sqlite/android \ + external/expat/lib \ + external/openssl/include \ + external/tremor/Tremor \ + external/icu4c/i18n \ + external/icu4c/common \ + +LOCAL_SHARED_LIBRARIES := \ + libexpat \ + libnativehelper \ + libcutils \ + libutils \ + libnetutils \ + libui \ + libsgl \ + libcorecg \ + libsqlite \ + libdvm \ + libGLES_CM \ + libhardware \ + libsonivox \ + libcrypto \ + libssl \ + libicuuc \ + libicui18n \ + libicudata \ + libmedia \ + libwpa_client + +ifeq ($(BOARD_HAVE_BLUETOOTH),true) +LOCAL_C_INCLUDES += \ + external/dbus \ + external/bluez/libs/include +LOCAL_CFLAGS += -DHAVE_BLUETOOTH +LOCAL_SHARED_LIBRARIES += libbluedroid libdbus +endif + +ifeq ($(TARGET_ARCH),arm) +LOCAL_SHARED_LIBRARIES += \ + libdl +endif + +LOCAL_LDLIBS += -lpthread -ldl + +ifeq ($(TARGET_OS),linux) +ifeq ($(TARGET_ARCH),x86) +LOCAL_LDLIBS += -lrt +endif +endif + +ifeq ($(WITH_MALLOC_LEAK_CHECK),true) + LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK +endif + +LOCAL_MODULE:= libandroid_runtime + +include $(BUILD_SHARED_LIBRARY) + + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp new file mode 100644 index 0000000..2a1184f --- /dev/null +++ b/core/jni/AndroidRuntime.cpp @@ -0,0 +1,1151 @@ +/* //device/libs/android_runtime/AndroidRuntime.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "AndroidRuntime" +//#define LOG_NDEBUG 0 + +#include <android_runtime/AndroidRuntime.h> +#include <utils/IBinder.h> +#include <utils/IServiceManager.h> +#include <utils/Log.h> +#include <utils/misc.h> +#include <utils/Parcel.h> +#include <utils/string_array.h> +#include <utils/threads.h> +#include <cutils/properties.h> + +#include <SkGraphics.h> +#include <SkImageDecoder.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_util_Binder.h" + +#include <stdio.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <signal.h> +#include <dirent.h> +#include <assert.h> + + +using namespace android; + +extern void register_BindTest(); + +extern int register_android_os_Binder(JNIEnv* env); +extern int register_android_os_Process(JNIEnv* env); +extern int register_android_graphics_Bitmap(JNIEnv*); +extern int register_android_graphics_BitmapFactory(JNIEnv*); +extern int register_android_graphics_Camera(JNIEnv* env); +extern int register_android_graphics_Graphics(JNIEnv* env); +extern int register_android_graphics_Interpolator(JNIEnv* env); +extern int register_android_graphics_LayerRasterizer(JNIEnv*); +extern int register_android_graphics_MaskFilter(JNIEnv* env); +extern int register_android_graphics_Movie(JNIEnv* env); +extern int register_android_graphics_NinePatch(JNIEnv*); +extern int register_android_graphics_PathEffect(JNIEnv* env); +extern int register_android_graphics_Region(JNIEnv* env); +extern int register_android_graphics_Shader(JNIEnv* env); +extern int register_android_graphics_Typeface(JNIEnv* env); + +extern int register_com_google_android_gles_jni_EGLImpl(JNIEnv* env); +extern int register_com_google_android_gles_jni_GLImpl(JNIEnv* env); + +extern int register_android_hardware_Camera(JNIEnv *env); + +extern int register_android_hardware_SensorManager(JNIEnv *env); + +extern int register_android_media_AudioSystem(JNIEnv *env); +extern int register_android_media_ToneGenerator(JNIEnv *env); + +extern int register_android_message_digest_sha1(JNIEnv *env); + +extern int register_android_util_FloatMath(JNIEnv* env); + +namespace android { + +/* + * JNI-based registration functions. Note these are properly contained in + * namespace android. + */ +extern int register_android_content_AssetManager(JNIEnv* env); +extern int register_android_util_EventLog(JNIEnv* env); +extern int register_android_util_Log(JNIEnv* env); +extern int register_android_content_StringBlock(JNIEnv* env); +extern int register_android_content_XmlBlock(JNIEnv* env); +extern int register_android_graphics_Canvas(JNIEnv* env); +extern int register_android_graphics_ColorFilter(JNIEnv* env); +extern int register_android_graphics_DrawFilter(JNIEnv* env); +extern int register_android_graphics_Matrix(JNIEnv* env); +extern int register_android_graphics_Paint(JNIEnv* env); +extern int register_android_graphics_Path(JNIEnv* env); +extern int register_android_graphics_PathMeasure(JNIEnv* env); +extern int register_android_graphics_Picture(JNIEnv*); +extern int register_android_graphics_PorterDuff(JNIEnv* env); +extern int register_android_graphics_Rasterizer(JNIEnv* env); +extern int register_android_graphics_Xfermode(JNIEnv* env); +extern int register_android_graphics_PixelFormat(JNIEnv* env); +extern int register_com_android_internal_graphics_NativeUtils(JNIEnv *env); +extern int register_android_view_Display(JNIEnv* env); +extern int register_android_view_Surface(JNIEnv* env); +extern int register_android_view_ViewRoot(JNIEnv* env); +extern int register_android_database_CursorWindow(JNIEnv* env); +extern int register_android_database_SQLiteDatabase(JNIEnv* env); +extern int register_android_database_SQLiteDebug(JNIEnv* env); +extern int register_android_database_SQLiteProgram(JNIEnv* env); +extern int register_android_database_SQLiteQuery(JNIEnv* env); +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_os_Debug(JNIEnv* env); +extern int register_android_os_ParcelFileDescriptor(JNIEnv *env); +extern int register_android_os_Power(JNIEnv *env); +extern int register_android_os_StatFs(JNIEnv *env); +extern int register_android_os_SystemProperties(JNIEnv *env); +extern int register_android_os_Hardware(JNIEnv* env); +extern int register_android_os_Exec(JNIEnv *env); +extern int register_android_os_SystemClock(JNIEnv* env); +extern int register_android_os_FileObserver(JNIEnv *env); +extern int register_android_os_FileUtils(JNIEnv *env); +extern int register_android_os_UEventObserver(JNIEnv* env); +extern int register_android_os_NetStat(JNIEnv* env); +extern int register_android_os_MemoryFile(JNIEnv* env); +extern int register_android_net_LocalSocketImpl(JNIEnv* env); +extern int register_android_net_NetworkUtils(JNIEnv* env); +extern int register_android_net_wifi_WifiManager(JNIEnv* env); +extern int register_android_security_Md5MessageDigest(JNIEnv *env); +extern int register_android_text_AndroidCharacter(JNIEnv *env); +extern int register_android_text_KeyCharacterMap(JNIEnv *env); +extern int register_android_opengl_classes(JNIEnv *env); +extern int register_android_bluetooth_Database(JNIEnv* env); +extern int register_android_bluetooth_HeadsetBase(JNIEnv* env); +extern int register_android_bluetooth_BluetoothAudioGateway(JNIEnv* env); +extern int register_android_bluetooth_RfcommSocket(JNIEnv *env); +extern int register_android_bluetooth_ScoSocket(JNIEnv *env); +extern int register_android_server_BluetoothDeviceService(JNIEnv* env); +extern int register_android_server_BluetoothEventLoop(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); +extern int register_android_location_GpsLocationProvider(JNIEnv* env); + +static AndroidRuntime* gCurRuntime = NULL; + +static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + if (jniThrowException(env, exc, msg) != 0) + assert(false); +} + +/* + * Code written in the Java Programming Language calls here from main(). + */ +static void com_android_internal_os_RuntimeInit_finishInit(JNIEnv* env, jobject clazz) +{ + gCurRuntime->onStarted(); +} + +static void com_android_internal_os_RuntimeInit_zygoteInit(JNIEnv* env, jobject clazz) +{ + gCurRuntime->onZygoteInit(); +} + +static jint com_android_internal_os_RuntimeInit_isComputerOn(JNIEnv* env, jobject clazz) +{ + return 1; +} + +static void com_android_internal_os_RuntimeInit_turnComputerOn(JNIEnv* env, jobject clazz) +{ +} + +static jint com_android_internal_os_RuntimeInit_getQwertyKeyboard(JNIEnv* env, jobject clazz) +{ + char* value = getenv("qwerty"); + if (value != NULL && strcmp(value, "true") == 0) { + return 1; + } + + return 0; +} + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + { "finishInit", "()V", + (void*) com_android_internal_os_RuntimeInit_finishInit }, + { "zygoteInitNative", "()V", + (void*) com_android_internal_os_RuntimeInit_zygoteInit }, + { "isComputerOn", "()I", + (void*) com_android_internal_os_RuntimeInit_isComputerOn }, + { "turnComputerOn", "()V", + (void*) com_android_internal_os_RuntimeInit_turnComputerOn }, + { "getQwertyKeyboard", "()I", + (void*) com_android_internal_os_RuntimeInit_getQwertyKeyboard }, +}; + +int register_com_android_internal_os_RuntimeInit(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit", + gMethods, NELEM(gMethods)); +} + +// ---------------------------------------------------------------------- + +/*static*/ JavaVM* AndroidRuntime::mJavaVM = NULL; + + +AndroidRuntime::AndroidRuntime() +{ + SkGraphics::Init(false); // true means run unittests (slow) + // this sets our preference for 16bit images during decode + // in case the src is opaque and 24bit + SkImageDecoder::SetDeviceConfig(SkBitmap::kRGB_565_Config); + + // Pre-allocate enough space to hold a fair number of options. + mOptions.setCapacity(20); + + assert(gCurRuntime == NULL); // one per process + gCurRuntime = this; +} + +AndroidRuntime::~AndroidRuntime() +{ + SkGraphics::Term(); +} + +/* + * Register native methods using JNI. + */ +/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env, + const char* className, const JNINativeMethod* gMethods, int numMethods) +{ + return jniRegisterNativeMethods(env, className, gMethods, numMethods); +} + +/* + * Call a static Java Programming Language function that takes no arguments and returns void. + */ +status_t AndroidRuntime::callStatic(const char* className, const char* methodName) +{ + JNIEnv* env; + jclass clazz; + jmethodID methodId; + + env = getJNIEnv(); + if (env == NULL) + return UNKNOWN_ERROR; + + clazz = findClass(env, className); + if (clazz == NULL) { + LOGE("ERROR: could not find class '%s'\n", className); + return UNKNOWN_ERROR; + } + methodId = env->GetStaticMethodID(clazz, methodName, "()V"); + if (methodId == NULL) { + LOGE("ERROR: could not find method %s.%s\n", className, methodName); + return UNKNOWN_ERROR; + } + + env->CallStaticVoidMethod(clazz, methodId); + + return NO_ERROR; +} + +status_t AndroidRuntime::callMain( + const char* className, int argc, const char* const argv[]) +{ + JNIEnv* env; + jclass clazz; + jmethodID methodId; + + env = getJNIEnv(); + if (env == NULL) + return UNKNOWN_ERROR; + + clazz = findClass(env, className); + if (clazz == NULL) { + LOGE("ERROR: could not find class '%s'\n", className); + return UNKNOWN_ERROR; + } + + methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V"); + if (methodId == NULL) { + LOGE("ERROR: could not find method %s.main(String[])\n", className); + return UNKNOWN_ERROR; + } + + /* + * We want to call main() with a String array with our arguments in it. + * Create an array and populate it. + */ + jclass stringClass; + jobjectArray strArray; + + stringClass = env->FindClass("java/lang/String"); + strArray = env->NewObjectArray(argc, stringClass, NULL); + + for (int i = 0; i < argc; i++) { + jstring argStr = env->NewStringUTF(argv[i]); + env->SetObjectArrayElement(strArray, i, argStr); + } + + env->CallStaticVoidMethod(clazz, methodId, strArray); + return NO_ERROR; +} + +/* + * Find the named class. + */ +jclass AndroidRuntime::findClass(JNIEnv* env, const char* className) +{ + char* convName = NULL; + + if (env->ExceptionCheck()) { + LOGE("ERROR: exception pending on entry to findClass()\n"); + return NULL; + } + + /* + * JNI FindClass uses class names with slashes, but ClassLoader.loadClass + * uses the dotted "binary name" format. We don't need to convert the + * name with the new approach. + */ +#if 0 + /* (convName only created if necessary -- use className) */ + for (char* cp = const_cast<char*>(className); *cp != '\0'; cp++) { + if (*cp == '.') { + if (convName == NULL) { + convName = strdup(className); + cp = convName + (cp-className); + className = convName; + } + *cp = '/'; + } + } +#endif + + /* + * This is a little awkward because the JNI FindClass call uses the + * class loader associated with the native method we're executing in. + * Because this native method is part of a "boot" class, JNI doesn't + * look for the class in CLASSPATH, which unfortunately is a likely + * location for it. (Had we issued the FindClass call before calling + * into the VM -- at which point there isn't a native method frame on + * the stack -- the VM would have checked CLASSPATH. We have to do + * this because we call into Java Programming Language code and + * bounce back out.) + * + * JNI lacks a "find class in a specific class loader" operation, so we + * have to do things the hard way. + */ + jclass cls = NULL; + //cls = env->FindClass(className); + + jclass javaLangClassLoader; + jmethodID getSystemClassLoader, loadClass; + jobject systemClassLoader; + jstring strClassName; + + /* find the "system" class loader; none of this is expected to fail */ + javaLangClassLoader = env->FindClass("java/lang/ClassLoader"); + assert(javaLangClassLoader != NULL); + getSystemClassLoader = env->GetStaticMethodID(javaLangClassLoader, + "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); + loadClass = env->GetMethodID(javaLangClassLoader, + "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + assert(getSystemClassLoader != NULL && loadClass != NULL); + systemClassLoader = env->CallStaticObjectMethod(javaLangClassLoader, + getSystemClassLoader); + assert(systemClassLoader != NULL); + + /* create an object for the class name string; alloc could fail */ + strClassName = env->NewStringUTF(className); + if (env->ExceptionCheck()) { + LOGE("ERROR: unable to convert '%s' to string\n", className); + goto bail; + } + LOGV("system class loader is %p, loading %p (%s)\n", + systemClassLoader, strClassName, className); + + /* try to find the named class */ + cls = (jclass) env->CallObjectMethod(systemClassLoader, loadClass, + strClassName); + if (env->ExceptionCheck()) { + LOGE("ERROR: unable to load class '%s' from %p\n", + className, systemClassLoader); + cls = NULL; + goto bail; + } + +bail: + free(convName); + return cls; +} + +/* + * The VM calls this through the "exit" hook. + */ +static void runtime_exit(int code) +{ + gCurRuntime->onExit(code); + exit(code); +} + +/* + * The VM calls this through the "vfprintf" hook. + * + * We ignore "fp" and just write the results to the log file. + */ +static void runtime_vfprintf(FILE* fp, const char* format, va_list ap) +{ + LOG_PRI_VA(ANDROID_LOG_INFO, "vm-printf", format, ap); +} + + +/** + * Add VM arguments to the to-be-executed VM + * Stops at first non '-' argument (also stops at an argument of '--') + * Returns the number of args consumed + */ +int AndroidRuntime::addVmArguments(int argc, const char* const argv[]) +{ + int i; + + for (i = 0; i<argc; i++) { + if (argv[i][0] != '-') { + return i; + } + if (argv[i][1] == '-' && argv[i][2] == 0) { + return i+1; + } + + JavaVMOption opt; + memset(&opt, 0, sizeof(opt)); + opt.optionString = (char*)argv[i]; + mOptions.add(opt); + } + return i; +} + +static int hasDir(const char* dir) +{ + struct stat s; + int res = stat(dir, &s); + if (res == 0) { + return S_ISDIR(s.st_mode); + } + return 0; +} + +/* + * We just want failed write() calls to just return with an error. + */ +static void blockSigpipe() +{ + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, SIGPIPE); + if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0) + LOGW("WARNING: SIGPIPE not blocked\n"); +} + +/* + * Read the persistent locale from file. + */ +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) { + /* 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); + } + + //LOGD("language=%s region=%s\n", language, region); +} + +void AndroidRuntime::start(const char* className, const bool startSystemServer) +{ + LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n"); + + JNIEnv* env; + JavaVMInitArgs initArgs; + JavaVMOption opt; + char propBuf[PROPERTY_VALUE_MAX]; + char enableAssertBuf[4 + PROPERTY_VALUE_MAX]; + char stackTraceFileBuf[PROPERTY_VALUE_MAX]; + char* stackTraceFile = NULL; + char* slashClassName = NULL; + char* cp; + bool checkJni = false; + bool logStdio = false; + bool verifyJava = true; + enum { kEMDefault, kEMIntPortable, kEMIntFast } executionMode = kEMDefault; + + blockSigpipe(); + + /* + * 'startSystemServer == true' means runtime is obslete and not run from + * init.rc anymore, so we print out the boot start event here. + */ + if (startSystemServer) { + /* track our progress through the boot sequence */ + const int LOG_BOOT_PROGRESS_START = 3000; + LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, + ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); + } + + property_get("dalvik.vm.checkjni", propBuf, ""); + if (strcmp(propBuf, "true") == 0) { + checkJni = true; + } else if (strcmp(propBuf, "false") != 0) { + /* property is neither true nor false; fall back on kernel parameter */ + property_get("ro.kernel.android.checkjni", propBuf, ""); + if (propBuf[0] == '1') { + checkJni = true; + } + } + + property_get("dalvik.vm.verify-bytecode", propBuf, ""); + if (strcmp(propBuf, "true") == 0) { + verifyJava = true; + } else if (strcmp(propBuf, "false") == 0) { + verifyJava = false; + } else { + /* bad value or not defined; use default */ + } + + property_get("dalvik.vm.execution-mode", propBuf, ""); + if (strcmp(propBuf, "int:portable") == 0) { + executionMode = kEMIntPortable; + } else if (strcmp(propBuf, "int:fast") == 0) { + executionMode = kEMIntFast; + } + + property_get("dalvik.vm.stack-trace-file", stackTraceFileBuf, ""); + + property_get("log.redirect-stdio", propBuf, ""); + if (strcmp(propBuf, "true") == 0) { + logStdio = true; + } + + strcpy(enableAssertBuf, "-ea:"); + property_get("dalvik.vm.enableassertions", enableAssertBuf+4, ""); + + const char* rootDir = getenv("ANDROID_ROOT"); + if (rootDir == NULL) { + rootDir = "/system"; + if (!hasDir("/system")) { + LOG_FATAL("No root directory specified, and /android does not exist."); + return; + } + setenv("ANDROID_ROOT", rootDir, 1); + } + + const char* kernelHack = getenv("LD_ASSUME_KERNEL"); + //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack); + + /* route exit() to our handler */ + opt.extraInfo = (void*) runtime_exit; + opt.optionString = "exit"; + mOptions.add(opt); + + /* route fprintf() to our handler */ + opt.extraInfo = (void*) runtime_vfprintf; + opt.optionString = "vfprintf"; + mOptions.add(opt); + + opt.extraInfo = NULL; + + /* enable verbose; standard options are { jni, gc, class } */ + //options[curOpt++].optionString = "-verbose:jni"; + opt.optionString = "-verbose:gc"; + mOptions.add(opt); + //options[curOpt++].optionString = "-verbose:class"; + + /* limit memory use to 16MB */ + opt.optionString = "-Xmx16m"; + 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. + * + * This should be coordinated with: + * //device/dalvik/libcore/android/src/main/native/dalvik_system_TouchDex.cpp + */ + if (verifyJava) { + opt.optionString = "-Xverify:all"; + mOptions.add(opt); + opt.optionString = "-Xdexopt:verified"; + mOptions.add(opt); + } else { + opt.optionString = "-Xverify:none"; + mOptions.add(opt); + //opt.optionString = "-Xdexopt:all"; + opt.optionString = "-Xdexopt:verified"; + mOptions.add(opt); + } + + /* enable debugging; set suspend=y to pause during VM init */ +#ifdef HAVE_ANDROID_OS + /* use android ADB transport */ + opt.optionString = + "-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y"; +#else + /* use TCP socket; address=0 means start at port 8000 and probe up */ + LOGI("Using TCP socket for JDWP\n"); + opt.optionString = + "-agentlib:jdwp=transport=dt_socket,suspend=n,server=y,address=0"; +#endif + mOptions.add(opt); + + char enableDPBuf[sizeof("-Xdeadlockpredict:") + PROPERTY_VALUE_MAX]; + property_get("dalvik.vm.deadlock-predict", propBuf, ""); + if (strlen(propBuf) > 0) { + strcpy(enableDPBuf, "-Xdeadlockpredict:"); + strcat(enableDPBuf, propBuf); + opt.optionString = enableDPBuf; + mOptions.add(opt); + } + + LOGD("CheckJNI is %s\n", checkJni ? "ON" : "OFF"); + if (checkJni) { + /* extended JNI checking */ + opt.optionString = "-Xcheck:jni"; + mOptions.add(opt); + + /* set a cap on JNI global references */ + opt.optionString = "-Xjnigreflimit:2000"; + mOptions.add(opt); + + /* with -Xcheck:jni, this provides a JNI function call trace */ + //opt.optionString = "-verbose:jni"; + //mOptions.add(opt); + } + if (executionMode == kEMIntPortable) { + opt.optionString = "-Xint:portable"; + mOptions.add(opt); + } else if (executionMode == kEMIntFast) { + opt.optionString = "-Xint:fast"; + mOptions.add(opt); + } + if (logStdio) { + /* convert stdout/stderr to log messages */ + opt.optionString = "-Xlog-stdio"; + mOptions.add(opt); + } + + if (enableAssertBuf[4] != '\0') { + /* accept "all" to mean "all classes and packages" */ + if (strcmp(enableAssertBuf+4, "all") == 0) + enableAssertBuf[3] = '\0'; + LOGI("Assertions enabled: '%s'\n", enableAssertBuf); + opt.optionString = enableAssertBuf; + mOptions.add(opt); + } else { + LOGV("Assertions disabled\n"); + } + + if (stackTraceFileBuf[0] != '\0') { + static const char* stfOptName = "-Xstacktracefile:"; + + stackTraceFile = (char*) malloc(strlen(stfOptName) + + strlen(stackTraceFileBuf) +1); + strcpy(stackTraceFile, stfOptName); + strcat(stackTraceFile, stackTraceFileBuf); + opt.optionString = stackTraceFile; + mOptions.add(opt); + } + + /* Set the properties for locale */ + { + char langOption[sizeof("-Duser.language=") + 3]; + char regionOption[sizeof("-Duser.region=") + 3]; + strcpy(langOption, "-Duser.language="); + strcpy(regionOption, "-Duser.region="); + readLocale(langOption, regionOption); + opt.extraInfo = NULL; + opt.optionString = langOption; + mOptions.add(opt); + opt.optionString = regionOption; + mOptions.add(opt); + } + + /* + * We don't have /tmp on the device, but we often have an SD card. Apps + * shouldn't use this, but some test suites might want to exercise it. + */ + opt.optionString = "-Djava.io.tmpdir=/sdcard"; + mOptions.add(opt); + + initArgs.version = JNI_VERSION_1_4; + initArgs.options = mOptions.editArray(); + initArgs.nOptions = mOptions.size(); + initArgs.ignoreUnrecognized = JNI_FALSE; + + /* + * Initialize the VM. + * + * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread. + * If this call succeeds, the VM is ready, and we can start issuing + * JNI calls. + */ + if (JNI_CreateJavaVM(&mJavaVM, &env, &initArgs) < 0) { + LOGE("JNI_CreateJavaVM failed\n"); + goto bail; + } + + /* + * Register android functions. + */ + if (startReg(env) < 0) { + LOGE("Unable to register all android natives\n"); + goto bail; + } + + /* + * We want to call main() with a String array with arguments in it. + * At present we only have one argument, the class name. Create an + * array to hold it. + */ + jclass stringClass; + jobjectArray strArray; + jstring classNameStr; + jstring startSystemServerStr; + + stringClass = env->FindClass("java/lang/String"); + assert(stringClass != NULL); + strArray = env->NewObjectArray(2, stringClass, NULL); + assert(strArray != NULL); + classNameStr = env->NewStringUTF(className); + assert(classNameStr != NULL); + env->SetObjectArrayElement(strArray, 0, classNameStr); + startSystemServerStr = env->NewStringUTF(startSystemServer ? + "true" : "false"); + env->SetObjectArrayElement(strArray, 1, startSystemServerStr); + + /* + * Start VM. This thread becomes the main thread of the VM, and will + * not return until the VM exits. + */ + jclass startClass; + jmethodID startMeth; + + slashClassName = strdup(className); + for (cp = slashClassName; *cp != '\0'; cp++) + if (*cp == '.') + *cp = '/'; + + startClass = env->FindClass(slashClassName); + if (startClass == NULL) { + LOGE("JavaVM unable to locate class '%s'\n", slashClassName); + /* keep going */ + } else { + startMeth = env->GetStaticMethodID(startClass, "main", + "([Ljava/lang/String;)V"); + if (startMeth == NULL) { + LOGE("JavaVM unable to find main() in '%s'\n", className); + /* keep going */ + } else { + env->CallStaticVoidMethod(startClass, startMeth, strArray); + +#if 0 + if (env->ExceptionCheck()) + threadExitUncaughtException(env); +#endif + } + } + + LOGD("Shutting down VM\n"); + if (mJavaVM->DetachCurrentThread() != JNI_OK) + LOGW("Warning: unable to detach main thread\n"); + if (mJavaVM->DestroyJavaVM() != 0) + LOGW("Warning: VM did not shut down cleanly\n"); + +bail: + free(slashClassName); + free(stackTraceFile); +} + +void AndroidRuntime::start() +{ + start("com.android.internal.os.RuntimeInit", + false /* Don't start the system server */); +} + +void AndroidRuntime::onExit(int code) +{ + LOGI("AndroidRuntime onExit calling exit(%d)", code); + exit(code); +} + +/* + * Get the JNIEnv pointer for this thread. + * + * Returns NULL if the slot wasn't allocated or populated. + */ +/*static*/ JNIEnv* AndroidRuntime::getJNIEnv() +{ + JNIEnv* env; + JavaVM* vm = AndroidRuntime::getJavaVM(); + assert(vm != NULL); + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) + return NULL; + return env; +} + +/* + * Makes the current thread visible to the VM. + * + * The JNIEnv pointer returned is only valid for the current thread, and + * thus must be tucked into thread-local storage. + */ +static int javaAttachThread(const char* threadName, JNIEnv** pEnv) +{ + JavaVMAttachArgs args; + JavaVM* vm; + jint result; + + vm = AndroidRuntime::getJavaVM(); + assert(vm != NULL); + + args.version = JNI_VERSION_1_4; + args.name = (char*) threadName; + args.group = NULL; + + result = vm->AttachCurrentThread(pEnv, (void*) &args); + if (result != JNI_OK) + LOGE("ERROR: thread attach failed\n"); + + return result; +} + +/* + * Detach the current thread from the set visible to the VM. + */ +static int javaDetachThread(void) +{ + JavaVM* vm; + jint result; + + vm = AndroidRuntime::getJavaVM(); + assert(vm != NULL); + + result = vm->DetachCurrentThread(); + if (result != JNI_OK) + LOGE("ERROR: thread detach failed\n"); + return result; +} + +/* + * When starting a native thread that will be visible from the VM, we + * bounce through this to get the right attach/detach action. + * Note that this function calls free(args) + */ +/*static*/ int AndroidRuntime::javaThreadShell(void* args) { + void* start = ((void**)args)[0]; + void* userData = ((void **)args)[1]; + char* name = (char*) ((void **)args)[2]; // we own this storage + free(args); + JNIEnv* env; + int result; + + /* hook us into the VM */ + if (javaAttachThread(name, &env) != JNI_OK) + return -1; + + /* start the thread running */ + result = (*(android_thread_func_t)start)(userData); + + /* unhook us */ + javaDetachThread(); + free(name); + + return result; +} + +/* + * This is invoked from androidCreateThreadEtc() via the callback + * set with androidSetCreateThreadFunc(). + * + * We need to create the new thread in such a way that it gets hooked + * into the VM before it really starts executing. + */ +/*static*/ int AndroidRuntime::javaCreateThreadEtc( + android_thread_func_t entryFunction, + void* userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t* threadId) +{ + void** args = (void**) malloc(3 * sizeof(void*)); // javaThreadShell must free + int result; + + assert(threadName != NULL); + + args[0] = (void*) entryFunction; + args[1] = userData; + args[2] = (void*) strdup(threadName); // javaThreadShell must free + + result = androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args, + threadName, threadPriority, threadStackSize, threadId); + return result; +} + +/* + * Create a thread that is visible from the VM. + * + * This is called from elsewhere in the library. + */ +/*static*/ void AndroidRuntime::createJavaThread(const char* name, + void (*start)(void *), void* arg) +{ + javaCreateThreadEtc((android_thread_func_t) start, arg, name, + ANDROID_PRIORITY_DEFAULT, 0, NULL); +} + +#if 0 +static void quickTest(void* arg) +{ + const char* str = (const char*) arg; + + printf("In quickTest: %s\n", str); +} +#endif + +#ifdef NDEBUG + #define REG_JNI(name) { name } + struct RegJNIRec { + int (*mProc)(JNIEnv*); + }; +#else + #define REG_JNI(name) { name, #name } + struct RegJNIRec { + int (*mProc)(JNIEnv*); + const char* mName; + }; +#endif + +typedef void (*RegJAMProc)(); + +static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env) +{ + for (size_t i = 0; i < count; i++) { + if (array[i].mProc(env) < 0) { +#ifndef NDEBUG + LOGD("----------!!! %s failed to load\n", array[i].mName); +#endif + return -1; + } + } + return 0; +} + +static void register_jam_procs(const RegJAMProc array[], size_t count) +{ + for (size_t i = 0; i < count; i++) { + array[i](); + } +} + +static const RegJNIRec gRegJNI[] = { + REG_JNI(register_android_debug_JNITest), + REG_JNI(register_com_android_internal_os_RuntimeInit), + REG_JNI(register_android_os_SystemClock), + 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_pim_EventRecurrence), + REG_JNI(register_android_content_AssetManager), + REG_JNI(register_android_content_StringBlock), + REG_JNI(register_android_content_XmlBlock), + REG_JNI(register_android_security_Md5MessageDigest), + REG_JNI(register_android_text_AndroidCharacter), + REG_JNI(register_android_text_KeyCharacterMap), + REG_JNI(register_android_os_Process), + REG_JNI(register_android_os_Binder), + REG_JNI(register_android_os_Hardware), + REG_JNI(register_android_view_Display), + REG_JNI(register_android_nio_utils), + REG_JNI(register_android_graphics_PixelFormat), + REG_JNI(register_android_graphics_Graphics), + REG_JNI(register_android_view_Surface), + REG_JNI(register_android_view_ViewRoot), + REG_JNI(register_com_google_android_gles_jni_EGLImpl), + REG_JNI(register_com_google_android_gles_jni_GLImpl), + + REG_JNI(register_android_graphics_Bitmap), + REG_JNI(register_android_graphics_BitmapFactory), + REG_JNI(register_android_graphics_Camera), + REG_JNI(register_android_graphics_Canvas), + REG_JNI(register_android_graphics_ColorFilter), + REG_JNI(register_android_graphics_DrawFilter), + REG_JNI(register_android_graphics_Interpolator), + REG_JNI(register_android_graphics_LayerRasterizer), + REG_JNI(register_android_graphics_MaskFilter), + REG_JNI(register_android_graphics_Matrix), + REG_JNI(register_android_graphics_Movie), + REG_JNI(register_android_graphics_NinePatch), + REG_JNI(register_android_graphics_Paint), + REG_JNI(register_android_graphics_Path), + REG_JNI(register_android_graphics_PathMeasure), + REG_JNI(register_android_graphics_PathEffect), + REG_JNI(register_android_graphics_Picture), + REG_JNI(register_android_graphics_PorterDuff), + REG_JNI(register_android_graphics_Rasterizer), + REG_JNI(register_android_graphics_Region), + REG_JNI(register_android_graphics_Shader), + REG_JNI(register_android_graphics_Typeface), + REG_JNI(register_android_graphics_Xfermode), + REG_JNI(register_com_android_internal_graphics_NativeUtils), + + REG_JNI(register_android_database_CursorWindow), + REG_JNI(register_android_database_SQLiteDatabase), + REG_JNI(register_android_database_SQLiteDebug), + REG_JNI(register_android_database_SQLiteProgram), + REG_JNI(register_android_database_SQLiteQuery), + REG_JNI(register_android_database_SQLiteStatement), + REG_JNI(register_android_os_Debug), + REG_JNI(register_android_os_Exec), + REG_JNI(register_android_os_FileObserver), + REG_JNI(register_android_os_FileUtils), + REG_JNI(register_android_os_ParcelFileDescriptor), + REG_JNI(register_android_os_Power), + REG_JNI(register_android_os_StatFs), + REG_JNI(register_android_os_SystemProperties), + REG_JNI(register_android_os_UEventObserver), + REG_JNI(register_android_net_LocalSocketImpl), + REG_JNI(register_android_net_NetworkUtils), + REG_JNI(register_android_net_wifi_WifiManager), + REG_JNI(register_android_os_NetStat), + REG_JNI(register_android_os_MemoryFile), + 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_AudioSystem), + REG_JNI(register_android_media_ToneGenerator), + + REG_JNI(register_android_opengl_classes), + REG_JNI(register_android_bluetooth_Database), + REG_JNI(register_android_bluetooth_HeadsetBase), + REG_JNI(register_android_bluetooth_BluetoothAudioGateway), + REG_JNI(register_android_bluetooth_RfcommSocket), + REG_JNI(register_android_bluetooth_ScoSocket), + REG_JNI(register_android_server_BluetoothDeviceService), + REG_JNI(register_android_server_BluetoothEventLoop), + REG_JNI(register_android_message_digest_sha1), + REG_JNI(register_android_ddm_DdmHandleNativeHeap), + REG_JNI(register_android_util_Base64), + REG_JNI(register_android_location_GpsLocationProvider), +}; + +/* + * Register android native functions with the VM. + */ +/*static*/ int AndroidRuntime::startReg(JNIEnv* env) +{ + /* + * This hook causes all future threads created in this process to be + * attached to the JavaVM. (This needs to go away in favor of JNI + * Attach calls.) + */ + androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc); + + LOGD("--- registering native functions ---\n"); + + /* + * Every "register" function calls one or more things that return + * a local reference (e.g. FindClass). Because we haven't really + * started the VM yet, they're all getting stored in the base frame + * and never released. Use Push/Pop to manage the storage. + */ + env->PushLocalFrame(200); + + if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) { + env->PopLocalFrame(NULL); + return -1; + } + env->PopLocalFrame(NULL); + + //createJavaThread("fubar", quickTest, (void*) "hello"); + + return 0; +} + +AndroidRuntime* AndroidRuntime::getRuntime() +{ + return gCurRuntime; +} + +/** + * Used by WithFramework to register native functions. + */ +extern "C" +jint Java_com_android_internal_util_WithFramework_registerNatives( + JNIEnv* env, jclass clazz) { + return register_jni_procs(gRegJNI, NELEM(gRegJNI), env); +} + +/** + * Used by LoadClass to register native functions. + */ +extern "C" +jint Java_LoadClass_registerNatives(JNIEnv* env, jclass clazz) { + return register_jni_procs(gRegJNI, NELEM(gRegJNI), env); +} + +} // namespace android diff --git a/core/jni/BindTest.cpp b/core/jni/BindTest.cpp new file mode 100644 index 0000000..5fae400 --- /dev/null +++ b/core/jni/BindTest.cpp @@ -0,0 +1,289 @@ +/* //device/libs/android_runtime/BindTest.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <jam-public.h> + +static u4 offset_instanceString; +static FieldBlock *fb_classString = NULL; +static Class *class_ReturnedObject = NULL; +static MethodBlock *mb_ReturnedObject_setReturnedString = NULL; +static MethodBlock *mb_Java_Lang_Object_Equals = NULL; + +static u4 offset_mObj; +static u4 offset_mBool; +static u4 offset_mInt; +static u4 offset_mString; +static u4 offset_mDouble; +static u4 offset_mLong; + + +/* native String getString(); */ +static uintptr_t * +getString(Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ + RETURN_OBJ (createString ("String")); +} + +/* native String getNullString(); */ +static uintptr_t * +getNullString(Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ + RETURN_OBJ (createString (NULL)); +} + +/* native String getBooleanTrue(); */ +static uintptr_t * +getBooleanTrue(Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ + RETURN_BOOLEAN (TRUE); +} + +/* native String getBooleanFalse(); */ +static uintptr_t * +getBooleanFalse(Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ + RETURN_BOOLEAN (FALSE); +} + +/* native Object nonvoidThrowsException() */ +static uintptr_t * +nonvoidThrowsException (Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ + if (1) { + signalException("java/lang/NullPointerException", NULL); + goto exception; + } + + RETURN_OBJ (NULL); +exception: + RETURN_VOID; +} + +/* native void setInstanceString(String s); */ +static uintptr_t * +setInstanceString (Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ + Object *jthis = (Object *) ostack[0]; + + JOBJ_set_obj(jthis, offset_instanceString, ostack[1]); + + RETURN_VOID; +} + +/* native void setClassString(String s) */ +static uintptr_t * +setClassString (Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ +// Object *jthis = (Object *) ostack[0]; + + fb_classString->static_value = ostack[1]; + + RETURN_VOID; +} + +/* native String makeStringFromThreeChars(char a, char b, char c); */ +static uintptr_t * +makeStringFromThreeChars (Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ + // Object *jthis = ostack[0]; + char a = (char) ostack[1]; + char b = (char) ostack[2]; + char c = (char) ostack[3]; + + char str[4]; + + str[0] = a; + str[1] = b; + str[2] = c; + str[3] = 0; + + RETURN_OBJ(createString(str)); +} + +/* native ReturnedObject makeReturnedObject(String a); */ +static uintptr_t * +makeReturnedObject (Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ + //Object *jthis = (Object*)ostack[0]; + Object *a = (Object*)ostack[1]; + + Object *ret; + + ret = allocObject(class_ReturnedObject); + + executeMethod(ret, mb_ReturnedObject_setReturnedString, a); + + RETURN_OBJ (ret); +} + +/* native double addDoubles(double a, double b); */ +static uintptr_t * +addDoubles (Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ + //Object *jthis = (Object*)ostack[0]; + double a = JARG_get_double(1); + double b = JARG_get_double(3); + + RETURN_DOUBLE(a+b); +} + +/* native void setAll (Object obj, boolean bool, int i, String str, double d, long l) */ +static uintptr_t * +setAll (Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ + Object *jthis = JARG_get_obj(0); + + Object *obj = JARG_get_obj(1); + bool b = JARG_get_bool(2); + int i = JARG_get_int(3); + char *str = JARG_get_cstr_strdup(4); + double d = JARG_get_double(5); + long long ll = JARG_get_long_long(5+2); + + JOBJ_set_obj(jthis, offset_mObj, obj); + JOBJ_set_bool(jthis, offset_mBool, b); + JOBJ_set_int(jthis, offset_mInt, i); + JOBJ_set_cstr(jthis, offset_mString, str); + free(str); + str = NULL; + JOBJ_set_double(jthis, offset_mDouble, d); + JOBJ_set_long_long(jthis, offset_mLong, ll); + + RETURN_VOID; +} + +/* native void compareAll (Object obj, boolean bool, int i, String str, double d, long l) */ +static uintptr_t * +compareAll (Class *clazz, MethodBlock *mb, uintptr_t *ostack) +{ + Object *jthis = JARG_get_obj(0); + + Object *obj = JARG_get_obj(1); + bool b = JARG_get_bool(2); + int i = JARG_get_int(3); + Object *strObj = JARG_get_obj(4); + double d = JARG_get_double(5); + long long ll = JARG_get_long_long(5+2); + + bool ret; + + void *result; + + Object *mStringObj = JOBJ_get_obj(jthis, offset_mString); + + char *s = JARG_get_cstr_strdup(4); + + result = executeMethod (strObj, lookupVirtualMethod(strObj,mb_Java_Lang_Object_Equals), + JOBJ_get_obj(jthis, offset_mString)); + + if (exceptionOccurred()) { + RETURN_VOID; + } + + ret = (*(uintptr_t *)result != 0) + && (obj == JOBJ_get_obj(jthis, offset_mObj)) + && (b == JOBJ_get_bool(jthis, offset_mBool)) + && (i == JOBJ_get_int(jthis, offset_mInt)) + && (d == JOBJ_get_double(jthis, offset_mDouble)) + && (ll == JOBJ_get_long_long(jthis, offset_mLong)); + + + RETURN_BOOLEAN(ret); +} + +static VMMethod methods[] = { + {"getString", getString}, + {"getNullString", getNullString}, + {"getBooleanTrue", getBooleanTrue}, + {"getBooleanFalse", getBooleanFalse}, + {"nonvoidThrowsException", nonvoidThrowsException}, + {"setInstanceString", setInstanceString}, + {"setClassString", setClassString}, + {"makeStringFromThreeChars", makeStringFromThreeChars}, + {"makeReturnedObject", makeReturnedObject}, + {"addDoubles", addDoubles}, + {"setAll", setAll}, + {"compareAll", compareAll}, + {NULL, NULL} +}; + + +void register_BindTest() +{ + jamvm_registerClass("BindTest", methods); + + Class *clazz = NULL; + + clazz = findClassFromClassLoader("BindTest", getSystemClassLoader()); + + if (clazz == NULL) { + fprintf(stderr, "Error: BindTest not found\n"); + clearException(); + return; + } + + FieldBlock *fb; + + fb = findField(clazz, "instanceString", "Ljava/lang/String;"); + + if (fb == NULL || ((fb->access_flags & ACC_STATIC) == ACC_STATIC)) { + fprintf(stderr, "Error: BindTest.instanceString not found or error\n"); + return; + } + + offset_instanceString = fb->offset; + + fb_classString = findField(clazz, "classString", "Ljava/lang/String;"); + + if (fb_classString == NULL || ((fb_classString->access_flags & ACC_STATIC) != ACC_STATIC)) { + fprintf(stderr, "Error: BindTest.classString not found or error\n"); + return; + } + + + class_ReturnedObject = findClassFromClassLoader("ReturnedObject", getSystemClassLoader()); + + if (class_ReturnedObject == NULL) { + fprintf(stderr, "Error: ReturnedObject class not found or error\n"); + return; + } + + mb_ReturnedObject_setReturnedString= + findMethod (class_ReturnedObject, "setReturnedString", "(Ljava/lang/String;)V"); + + if (mb_ReturnedObject_setReturnedString == NULL) { + fprintf(stderr, "Error: ReturnedObject.setReturnedString class not found or error\n"); + return; + } + + offset_mObj = findField(clazz, "mObj", "Ljava/lang/Object;")->offset; + offset_mBool = findField(clazz, "mBool", "Z" )->offset; + offset_mInt = findField(clazz, "mInt", "I")->offset; + offset_mString = findField(clazz, "mString", "Ljava/lang/String;")->offset; + offset_mDouble = findField(clazz, "mDouble", "D")->offset; + offset_mLong = findField(clazz, "mLong", "J")->offset; + + + mb_Java_Lang_Object_Equals = findMethod ( + findClassFromClassLoader("java/lang/Object", getSystemClassLoader()), + "equals", "(Ljava/lang/Object;)Z"); + +} + + diff --git a/core/jni/CursorWindow.cpp b/core/jni/CursorWindow.cpp new file mode 100644 index 0000000..fb891c9 --- /dev/null +++ b/core/jni/CursorWindow.cpp @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2006-2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "CursorWindow" + +#include <utils/Log.h> +#include <utils/MemoryDealer.h> + +#include <assert.h> +#include <string.h> +#include <stdlib.h> + +#include <jni.h> +#include <JNIHelp.h> + +#include "CursorWindow.h" + + +namespace android { + +CursorWindow::CursorWindow(size_t maxSize) : + mMaxSize(maxSize) +{ +} + +bool CursorWindow::setMemory(sp<IMemory> memory) +{ + mMemory = memory; + mData = (uint8_t *) memory->pointer(); + if (mData == NULL) { + return false; + } + mHeader = (window_header_t *) mData; + + // Make the window read-only + mHeap = NULL; + ssize_t size = memory->size(); + mSize = size; + mMaxSize = size; + mFreeOffset = size; +LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData); + return true; +} + +bool CursorWindow::initBuffer(bool localOnly) +{ + //TODO Use a non-memory dealer mmap region for localOnly + + mHeap = new MemoryDealer(new SharedHeap(mMaxSize, 0, "CursorWindow")); + if (mHeap != NULL) { + mMemory = mHeap->allocate(mMaxSize); + if (mMemory != NULL) { + mData = (uint8_t *) mMemory->pointer(); + if (mData) { + mHeader = (window_header_t *) mData; + mSize = mMaxSize; + + // Put the window into a clean state + clear(); + LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData); + return true; + } + } + LOGE("memory dealer allocation failed"); + return false; + } else { + LOGE("failed to create the memory dealer"); + return false; + } +} + +CursorWindow::~CursorWindow() +{ + // Everything that matters is a smart pointer +} + +void CursorWindow::clear() +{ + mHeader->numRows = 0; + mHeader->numColumns = 0; + mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE; + // Mark the first chunk's next 'pointer' as null + *((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0; +} + +int32_t CursorWindow::freeSpace() +{ + int32_t freeSpace = mSize - mFreeOffset; + if (freeSpace < 0) { + freeSpace = 0; + } + return freeSpace; +} + +field_slot_t * CursorWindow::allocRow() +{ + // Fill in the row slot + row_slot_t * rowSlot = allocRowSlot(); + if (rowSlot == NULL) { + return NULL; + } + + // Allocate the slots for the field directory + size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t); + uint32_t fieldDirOffset = alloc(fieldDirSize); + if (!fieldDirOffset) { + mHeader->numRows--; + LOGE("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows); + return NULL; + } + field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset); + memset(fieldDir, 0x0, fieldDirSize); + +LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset); + rowSlot->offset = fieldDirOffset; + + return fieldDir; +} + +uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned) +{ + int32_t size; + uint32_t padding; + if (aligned) { + // 4 byte alignment + padding = 4 - (mFreeOffset & 0x3); + } else { + padding = 0; + } + + size = requestedSize + padding; + + if (size > freeSpace()) { + LOGE("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size, freeSpace(), mHeader->numRows); + // Only grow the window if the first row doesn't fit + if (mHeader->numRows > 1) { +LOGE("not growing since there are already %d row(s), max size %d", mHeader->numRows, mMaxSize); + return 0; + } + + // Find a new size that will fit the allocation + int allocated = mSize - freeSpace(); + int newSize = mSize + WINDOW_ALLOCATION_SIZE; + while (size > (newSize - allocated)) { + newSize += WINDOW_ALLOCATION_SIZE; + if (newSize > mMaxSize) { + LOGE("Attempting to grow window beyond max size (%d)", mMaxSize); + return 0; + } + } +LOG_WINDOW("found size %d", newSize); + mSize = newSize; + } + + uint32_t offset = mFreeOffset + padding; + mFreeOffset += size; + return offset; +} + +row_slot_t * CursorWindow::getRowSlot(int row) +{ + LOG_WINDOW("enter getRowSlot current row num %d, this row %d", mHeader->numRows, row); + int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t); + uint8_t * rowChunk = mData + sizeof(window_header_t); + for (int i = 0; i < chunkNum; i++) { + rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset))); + chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)); + } + return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); + LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row); +} + +row_slot_t * CursorWindow::allocRowSlot() +{ + int chunkNum = mHeader->numRows / ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t); + uint8_t * rowChunk = mData + sizeof(window_header_t); +LOG_WINDOW("Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d", mHeader->numRows, chunkNum, chunkPos); + for (int i = 0; i < chunkNum; i++) { + uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset)); +LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset); + if (nextChunkOffset == 0) { + // Allocate a new row chunk + nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true); + if (nextChunkOffset == 0) { + return NULL; + } + rowChunk = offsetToPtr(nextChunkOffset); +LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk); + *((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData; + // Mark the new chunk's next 'pointer' as null + *((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0; + } else { +LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset); + rowChunk = offsetToPtr(nextChunkOffset); + chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)); + } + } + mHeader->numRows++; + + return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); +} + +field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column) +{ + if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) { + LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns); + return NULL; + } + row_slot_t * rowSlot = getRowSlot(row); + if (!rowSlot) { + LOGE("Failed to find rowSlot for row %d", row); + return NULL; + } + if (rowSlot->offset == 0 || rowSlot->offset >= mSize) { + LOGE("Invalid rowSlot, offset = %d", rowSlot->offset); + return NULL; + } + int fieldDirOffset = rowSlot->offset; + return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column; +} + +uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut) +{ + if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) { + LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns); + return -1; + } + row_slot_t * rowSlot = getRowSlot(row); + if (!rowSlot) { + LOGE("Failed to find rowSlot for row %d", row); + return -1; + } + if (rowSlot->offset == 0 || rowSlot->offset >= mSize) { + LOGE("Invalid rowSlot, offset = %d", rowSlot->offset); + return -1; + } +LOG_WINDOW("Found field directory for %d,%d at rowSlot %d, offset %d", row, column, (uint8_t *)rowSlot - mData, rowSlot->offset); + field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(rowSlot->offset); +LOG_WINDOW("Read field_slot_t %d,%d: offset = %d, size = %d, type = %d", row, column, fieldDir[column].data.buffer.offset, fieldDir[column].data.buffer.size, fieldDir[column].type); + + // Copy the data to the out param + slotOut->data.buffer.offset = fieldDir[column].data.buffer.offset; + slotOut->data.buffer.size = fieldDir[column].data.buffer.size; + slotOut->type = fieldDir[column].type; + return 0; +} + +void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size) +{ + assert(offset + size <= mSize); + memcpy(mData + offset, data, size); +} + +void CursorWindow::copyIn(uint32_t offset, int64_t data) +{ + assert(offset + sizeof(int64_t) <= mSize); + memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t)); +} + +void CursorWindow::copyIn(uint32_t offset, double data) +{ + assert(offset + sizeof(double) <= mSize); + memcpy(mData + offset, (uint8_t *)&data, sizeof(double)); +} + +void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size) +{ + assert(offset + size <= mSize); + memcpy(data, mData + offset, size); +} + +int64_t CursorWindow::copyOutLong(uint32_t offset) +{ + int64_t value; + assert(offset + sizeof(int64_t) <= mSize); + memcpy(&value, mData + offset, sizeof(int64_t)); + return value; +} + +double CursorWindow::copyOutDouble(uint32_t offset) +{ + double value; + assert(offset + sizeof(double) <= mSize); + memcpy(&value, mData + offset, sizeof(double)); + return value; +} + +bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot) { + return false; + } + +#if WINDOW_STORAGE_INLINE_NUMERICS + fieldSlot->data.l = value; +#else + int offset = alloc(sizeof(int64_t)); + if (!offset) { + return false; + } + + copyIn(offset, value); + + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = sizeof(int64_t); +#endif + fieldSlot->type = FIELD_TYPE_INTEGER; + return true; +} + +bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot) { + return false; + } + +#if WINDOW_STORAGE_INLINE_NUMERICS + fieldSlot->data.d = value; +#else + int offset = alloc(sizeof(int64_t)); + if (!offset) { + return false; + } + + copyIn(offset, value); + + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = sizeof(double); +#endif + fieldSlot->type = FIELD_TYPE_FLOAT; + return true; +} + +bool CursorWindow::putNull(unsigned int row, unsigned int col) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot) { + return false; + } + + fieldSlot->type = FIELD_TYPE_NULL; + fieldSlot->data.buffer.offset = 0; + fieldSlot->data.buffer.size = 0; + return true; +} + +bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOut) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) { + return false; + } + +#if WINDOW_STORAGE_INLINE_NUMERICS + *valueOut = fieldSlot->data.l; +#else + *valueOut = copyOutLong(fieldSlot->data.buffer.offset); +#endif + return true; +} + +bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueOut) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot || fieldSlot->type != FIELD_TYPE_FLOAT) { + return false; + } + +#if WINDOW_STORAGE_INLINE_NUMERICS + *valueOut = fieldSlot->data.d; +#else + *valueOut = copyOutDouble(fieldSlot->data.buffer.offset); +#endif + return true; +} + +bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot) { + return false; + } + + if (fieldSlot->type != FIELD_TYPE_NULL) { + *valueOut = false; + } else { + *valueOut = true; + } + return true; +} + +}; // namespace android diff --git a/core/jni/CursorWindow.h b/core/jni/CursorWindow.h new file mode 100644 index 0000000..0fb074f --- /dev/null +++ b/core/jni/CursorWindow.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID__DATABASE_WINDOW_H +#define _ANDROID__DATABASE_WINDOW_H + +#include <cutils/log.h> +#include <stddef.h> +#include <stdint.h> + +#include <utils/MemoryDealer.h> +#include <utils/RefBase.h> + +#include <jni.h> + +#define DEFAULT_WINDOW_SIZE 4096 +#define MAX_WINDOW_SIZE (1024 * 1024) +#define WINDOW_ALLOCATION_SIZE 4096 + +#define ROW_SLOT_CHUNK_NUM_ROWS 16 + +// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS, +// with an offset after the rows that points to the next chunk +#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t)) + + +#if LOG_NDEBUG + +#define IF_LOG_WINDOW() if (false) +#define LOG_WINDOW(...) + +#else + +#define IF_LOG_WINDOW() IF_LOG(LOG_DEBUG, "CursorWindow") +#define LOG_WINDOW(...) LOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__) + +#endif + + +// When defined to true strings are stored as UTF8, otherwise they're UTF16 +#define WINDOW_STORAGE_UTF8 1 + +// When defined to true numberic values are stored inline in the field_slot_t, otherwise they're allocated in the window +#define WINDOW_STORAGE_INLINE_NUMERICS 1 + +namespace android { + +typedef struct +{ + uint32_t numRows; + uint32_t numColumns; +} window_header_t; + +typedef struct +{ + uint32_t offset; +} row_slot_t; + +typedef struct +{ + uint8_t type; + union { + double d; + int64_t l; + struct { + uint32_t offset; + uint32_t size; + } buffer; + } data; +} __attribute__((packed)) field_slot_t; + +#define FIELD_TYPE_INTEGER 1 +#define FIELD_TYPE_FLOAT 2 +#define FIELD_TYPE_STRING 3 +#define FIELD_TYPE_BLOB 4 +#define FIELD_TYPE_NULL 5 + +/** + * This class stores a set of rows from a database in a buffer. The begining of the + * window has first chunk of row_slot_ts, which are offsets to the row directory, followed by + * an offset to the next chunk in a linked-list of additional chunk of row_slot_ts in case + * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a + * field_slot_t per column, which has the size, offset, and type of the data for that field. + * Note that the data types come from sqlite3.h. + */ +class CursorWindow +{ +public: + CursorWindow(size_t maxSize); + CursorWindow(){} + bool setMemory(sp<IMemory>); + ~CursorWindow(); + + bool initBuffer(bool localOnly); + sp<IMemory> getMemory() {return mMemory;} + + size_t size() {return mSize;} + uint8_t * data() {return mData;} + uint32_t getNumRows() {return mHeader->numRows;} + uint32_t getNumColumns() {return mHeader->numColumns;} + void freeLastRow() { + if (mHeader->numRows > 0) { + mHeader->numRows--; + } + } + bool setNumColumns(uint32_t numColumns) + { + uint32_t cur = mHeader->numColumns; + if (cur > 0 && cur != numColumns) { + LOGE("Trying to go from %d columns to %d", cur, numColumns); + return false; + } + mHeader->numColumns = numColumns; + return true; + } + + int32_t freeSpace(); + + void clear(); + + /** + * Allocate a row slot and its directory. The returned + * pointer points to the begining of the row's directory + * or NULL if there wasn't room. The directory is + * initialied with NULL entries for each field. + */ + field_slot_t * allocRow(); + + /** + * Allocate a portion of the window. Returns the offset + * of the allocation, or 0 if there isn't enough space. + * If aligned is true, the allocation gets 4 byte alignment. + */ + uint32_t alloc(size_t size, bool aligned = false); + + uint32_t read_field_slot(int row, int column, field_slot_t * slot); + + /** + * Copy data into the window at the given offset. + */ + void copyIn(uint32_t offset, uint8_t const * data, size_t size); + void copyIn(uint32_t offset, int64_t data); + void copyIn(uint32_t offset, double data); + + void copyOut(uint32_t offset, uint8_t * data, size_t size); + int64_t copyOutLong(uint32_t offset); + double copyOutDouble(uint32_t offset); + + bool putLong(unsigned int row, unsigned int col, int64_t value); + bool putDouble(unsigned int row, unsigned int col, double value); + bool putNull(unsigned int row, unsigned int col); + + bool getLong(unsigned int row, unsigned int col, int64_t * valueOut); + bool getDouble(unsigned int row, unsigned int col, double * valueOut); + bool getNull(unsigned int row, unsigned int col, bool * valueOut); + + uint8_t * offsetToPtr(uint32_t offset) {return mData + offset;} + + row_slot_t * allocRowSlot(); + + row_slot_t * getRowSlot(int row); + + /** + * return NULL if Failed to find rowSlot or + * Invalid rowSlot + */ + field_slot_t * getFieldSlotWithCheck(int row, int column); + field_slot_t * getFieldSlot(int row, int column) + { + int fieldDirOffset = getRowSlot(row)->offset; + return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column; + } + +private: + uint8_t * mData; + size_t mSize; + size_t mMaxSize; + window_header_t * mHeader; + sp<MemoryDealer> mHeap; + sp<IMemory> mMemory; + + /** + * Offset of the lowest unused data byte in the array. + */ + uint32_t mFreeOffset; +}; + +}; // namespace android + +#endif diff --git a/core/jni/GraphicsExternGlue.h b/core/jni/GraphicsExternGlue.h new file mode 100644 index 0000000..290dbd5 --- /dev/null +++ b/core/jni/GraphicsExternGlue.h @@ -0,0 +1,25 @@ +/* //device/libs/android_runtime/GraphicsExternGlue.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +extern void register_android_Paint(); +extern void register_android_Canvas(); +extern void register_android_ColorFilter(); +extern void register_android_Matrix(); +extern void register_android_Path(); +extern void register_android_PorterDuff(); +extern void register_android_Rasterizer(); +extern void register_android_Xfermode(); diff --git a/core/jni/GraphicsRegisterGlue.h b/core/jni/GraphicsRegisterGlue.h new file mode 100644 index 0000000..4054e83 --- /dev/null +++ b/core/jni/GraphicsRegisterGlue.h @@ -0,0 +1,25 @@ +/* //device/libs/android_runtime/GraphicsRegisterGlue.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +register_android_Paint(); +register_android_Canvas(); +register_android_ColorFilter(); +register_android_Matrix(); +register_android_Path(); +register_android_PorterDuff(); +register_android_Rasterizer(); +register_android_Xfermode(); diff --git a/core/jni/MODULE_LICENSE_APACHE2 b/core/jni/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/core/jni/MODULE_LICENSE_APACHE2 diff --git a/core/jni/NOTICE b/core/jni/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/core/jni/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp new file mode 100644 index 0000000..8628ec0 --- /dev/null +++ b/core/jni/android/graphics/Bitmap.cpp @@ -0,0 +1,573 @@ +#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)); +} + diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp new file mode 100644 index 0000000..90822a1 --- /dev/null +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -0,0 +1,505 @@ +#include "SkImageDecoder.h" +#include "SkPixelRef.h" +#include "SkStream.h" +#include "GraphicsJNI.h" +#include "SkTemplates.h" +#include "SkUtils.h" +#include "CreateJavaOutputStreamAdaptor.h" + +#include <android_runtime/AndroidRuntime.h> +#include <utils/Asset.h> +#include <utils/ResourceTypes.h> +#include <netinet/in.h> +#include <sys/mman.h> + +static jclass gOptions_class; +static jfieldID gOptions_justBoundsFieldID; +static jfieldID gOptions_sampleSizeFieldID; +static jfieldID gOptions_configFieldID; +static jfieldID gOptions_ditherFieldID; +static jfieldID gOptions_widthFieldID; +static jfieldID gOptions_heightFieldID; +static jfieldID gOptions_mimeFieldID; + +static jclass gFileDescriptor_class; +static jfieldID gFileDescriptor_descriptor; + +#if 0 + #define TRACE_BITMAP(code) code +#else + #define TRACE_BITMAP(code) +#endif + +//#define MIN_SIZE_TO_USE_MMAP (4*1024) + +/////////////////////////////////////////////////////////////////////////////// + +class AutoDecoderCancel { +public: + AutoDecoderCancel(jobject options, SkImageDecoder* decoder); + ~AutoDecoderCancel(); + + static bool RequestCancel(jobject options); + +private: + AutoDecoderCancel* fNext; + jobject fJOptions; // java options object + SkImageDecoder* fDecoder; +}; + +static SkMutex gAutoDecoderCancelMutex; +static AutoDecoderCancel* gAutoDecoderCancel; + +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); + + fNext = gAutoDecoderCancel; + gAutoDecoderCancel = this; + } +} + +AutoDecoderCancel::~AutoDecoderCancel() { + const jobject joptions = fJOptions; + + if (NULL != joptions) { + 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; + } + SkDebugf("xxxxxxxxxxxxxxxxxxxxxxx not found in pair list %p %p\n", + fJOptions, fDecoder); + } +} + +bool AutoDecoderCancel::RequestCancel(jobject joptions) { + SkAutoMutexAcquire ac(gAutoDecoderCancelMutex); + + AutoDecoderCancel* pair = gAutoDecoderCancel; + while (pair != NULL) { + if (pair->fJOptions == joptions) { + pair->fDecoder->cancelDecode(); + return true; + } + pair = pair->fNext; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +using namespace android; + +class NinePatchPeeker : public SkImageDecoder::Peeker { +public: + NinePatchPeeker() { + fPatchIsValid = false; + } + + ~NinePatchPeeker() { + if (fPatchIsValid) { + free(fPatch); + } + } + + bool fPatchIsValid; + Res_png_9patch* fPatch; + + virtual bool peek(const char tag[], const void* data, size_t length) { + if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) { + Res_png_9patch* patch = (Res_png_9patch*) data; + size_t patchSize = patch->serializedSize(); + assert(length == patchSize); + // 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); + Res_png_9patch::deserialize(patchNew); + patchNew->fileToDevice(); + if (fPatchIsValid) { + free(fPatch); + } + fPatch = patchNew; + //printf("9patch: (%d,%d)-(%d,%d)\n", + // fPatch.sizeLeft, fPatch.sizeTop, + // fPatch.sizeRight, fPatch.sizeBottom); + fPatchIsValid = true; + } else { + fPatch = NULL; + } + return true; // keep on decoding + } +}; + +class AssetStreamAdaptor : public SkStream { +public: + AssetStreamAdaptor(Asset* a) : fAsset(a) {} + + virtual bool rewind() { + off_t pos = fAsset->seek(0, SEEK_SET); + return pos != (off_t)-1; + } + + virtual size_t read(void* buffer, size_t size) { + ssize_t amount; + + if (NULL == buffer) { + if (0 == size) { // caller is asking us for our total length + return fAsset->getLength(); + } + // asset->seek returns new total offset + // we want to return amount that was skipped + + off_t oldOffset = fAsset->seek(0, SEEK_CUR); + if (-1 == oldOffset) { + return 0; + } + off_t newOffset = fAsset->seek(size, SEEK_CUR); + if (-1 == newOffset) { + return 0; + } + amount = newOffset - oldOffset; + } else { + amount = fAsset->read(buffer, size); + } + + if (amount < 0) { + amount = 0; + } + return amount; + } + +private: + Asset* fAsset; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static inline int32_t validOrNeg1(bool isValid, int32_t value) { +// return isValid ? value : -1; + SkASSERT((int)isValid == 0 || (int)isValid == 1); + return ((int32_t)isValid - 1) | value; +} + +static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) { + static const struct { + SkImageDecoder::Format fFormat; + const char* fMimeType; + } gMimeTypes[] = { + { SkImageDecoder::kBMP_Format, "image/bmp" }, + { SkImageDecoder::kGIF_Format, "image/gif" }, + { SkImageDecoder::kICO_Format, "image/x-ico" }, + { SkImageDecoder::kJPEG_Format, "image/jpeg" }, + { SkImageDecoder::kPNG_Format, "image/png" }, + { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" } + }; + + const char* cstr = NULL; + for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) { + if (gMimeTypes[i].fFormat == format) { + cstr = gMimeTypes[i].fMimeType; + break; + } + } + + jstring jstr = 0; + if (NULL != cstr) { + jstr = env->NewStringUTF(cstr); + } + return jstr; +} + +static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, + jobject options) { + + int sampleSize = 1; + SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; + SkBitmap::Config prefConfig = SkBitmap::kNo_Config; + bool doDither = true; + + if (NULL != options) { + sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); + if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) { + mode = SkImageDecoder::kDecodeBounds_Mode; + } + // initialize these, in case we fail later on + env->SetIntField(options, gOptions_widthFieldID, -1); + env->SetIntField(options, gOptions_heightFieldID, -1); + env->SetObjectField(options, gOptions_mimeFieldID, 0); + + jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); + prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); + doDither = env->GetBooleanField(options, gOptions_ditherFieldID); + } + + SkImageDecoder* decoder = SkImageDecoder::Factory(stream); + if (NULL == decoder) { + return NULL; + } + + decoder->setSampleSize(sampleSize); + decoder->setDitherImage(doDither); + + NinePatchPeeker peeker; + JavaPixelAllocator allocator(env); + SkBitmap* bitmap = new SkBitmap; + Res_png_9patch dummy9Patch; + + SkAutoTDelete<SkImageDecoder> add(decoder); + SkAutoTDelete<SkBitmap> adb(bitmap); + + decoder->setPeeker(&peeker); + decoder->setAllocator(&allocator); + + AutoDecoderCancel adc(options, decoder); + + if (!decoder->decode(stream, bitmap, prefConfig, mode)) { + return NULL; + } + + // update options (if any) + if (NULL != options) { + env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); + env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); + // TODO: set the mimeType field with the data from the codec. + // but how to reuse a set of strings, rather than allocating new one + // each time? + env->SetObjectField(options, gOptions_mimeFieldID, + getMimeTypeString(env, decoder->getFormat())); + } + + // if we're in justBounds mode, return now (skip the java bitmap) + if (SkImageDecoder::kDecodeBounds_Mode == mode) { + return NULL; + } + + jbyteArray ninePatchChunk = NULL; + if (peeker.fPatchIsValid) { + size_t ninePatchArraySize = peeker.fPatch->serializedSize(); + ninePatchChunk = env->NewByteArray(ninePatchArraySize); + if (NULL == ninePatchChunk) { + return NULL; + } + jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk, + NULL); + if (NULL == array) { + return NULL; + } + peeker.fPatch->serialize(array); + env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); + } + + // detach bitmap from its autotdeleter, since we want to own it now + adb.detach(); + + if (padding) { + if (peeker.fPatchIsValid) { + GraphicsJNI::set_jrect(env, padding, + peeker.fPatch->paddingLeft, + peeker.fPatch->paddingTop, + peeker.fPatch->paddingRight, + peeker.fPatch->paddingBottom); + } else { + GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); + } + } + + // promise we will never change our pixels (great for sharing and pictures) + SkPixelRef* ref = bitmap->pixelRef(); + SkASSERT(ref); + ref->setImmutable(); + + return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk); +} + +static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, + jobject is, // InputStream + jbyteArray storage, // byte[] + jobject padding, + jobject options) { // BitmapFactory$Options + jobject bitmap = NULL; + SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage); + + if (stream) { + bitmap = doDecode(env, stream, padding, options); + stream->unref(); + } + return bitmap; +} + +static ssize_t getFDSize(int fd) { + off_t curr = ::lseek(fd, 0, SEEK_CUR); + if (curr < 0) { + return 0; + } + size_t size = ::lseek(fd, 0, SEEK_END); + ::lseek(fd, curr, SEEK_SET); + return size; +} + +/** Restore the file descriptor's offset in our destructor + */ +class AutoFDSeek { +public: + AutoFDSeek(int fd) : fFD(fd) { + fCurr = ::lseek(fd, 0, SEEK_CUR); + } + ~AutoFDSeek() { + if (fCurr >= 0) { + ::lseek(fFD, fCurr, SEEK_SET); + } + } +private: + int fFD; + off_t fCurr; +}; + +static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, + jobject fileDescriptor, + jobject padding, + jobject bitmapFactoryOptions) { + NPE_CHECK_RETURN_ZERO(env, fileDescriptor); + + jint descriptor = env->GetIntField(fileDescriptor, + gFileDescriptor_descriptor); + +#ifdef MIN_SIZE_TO_USE_MMAP + // First try to use mmap + size_t size = getFDSize(descriptor); + if (size >= MIN_SIZE_TO_USE_MMAP) { + void* addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, descriptor, 0); +// SkDebugf("-------- mmap returned %p %d\n", addr, size); + if (MAP_FAILED != addr) { + SkMemoryStream strm(addr, size); + jobject obj = doDecode(env, &strm, padding, bitmapFactoryOptions); + munmap(addr, size); + return obj; + } + } +#endif + + // we pass false for closeWhenDone, since the caller owns the descriptor + SkFDStream file(descriptor, false); + if (!file.isValid()) { + return NULL; + } + + /* Restore our offset when we leave, so the caller doesn't have to. + This is a real feature, so we can be called more than once with the + same descriptor. + */ + AutoFDSeek as(descriptor); + + return doDecode(env, &file, padding, bitmapFactoryOptions); +} + +static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, + jint native_asset, // Asset + jobject padding, // Rect + jobject options) { // BitmapFactory$Options + AssetStreamAdaptor mystream((Asset*)native_asset); + + return doDecode(env, &mystream, padding, options); +} + +static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, + int offset, int length, jobject options) { + AutoJavaByteArray ar(env, byteArray); + SkMemoryStream stream(ar.ptr() + offset, length); + + return doDecode(env, &stream, NULL, options); +} + +static void nativeRequestCancel(JNIEnv*, jobject joptions) { + (void)AutoDecoderCancel::RequestCancel(joptions); +} + +/////////////////////////////////////////////////////////////////////////////// + +static JNINativeMethod gMethods[] = { + { "nativeDecodeStream", + "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", + (void*)nativeDecodeStream + }, + + { "nativeDecodeFileDescriptor", + "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", + (void*)nativeDecodeFileDescriptor + }, + + { "nativeDecodeAsset", + "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", + (void*)nativeDecodeAsset + }, + + { "nativeDecodeByteArray", + "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", + (void*)nativeDecodeByteArray + } +}; + +static JNINativeMethod gOptionsMethods[] = { + { "requestCancel", "()V", (void*)nativeRequestCancel } +}; + +static jclass make_globalref(JNIEnv* env, const char classname[]) { + jclass c = env->FindClass(classname); + SkASSERT(c); + return (jclass)env->NewGlobalRef(c); +} + +static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, + const char fieldname[], const char type[]) { + jfieldID id = env->GetFieldID(clazz, fieldname, type); + SkASSERT(id); + return id; +} + +#define kClassPathName "android/graphics/BitmapFactory" + +#define RETURN_ERR_IF_NULL(value) \ + do { if (!(value)) { assert(0); return -1; } } while (false) + +int register_android_graphics_BitmapFactory(JNIEnv* env); +int register_android_graphics_BitmapFactory(JNIEnv* env) { + gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options"); + gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z"); + gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I"); + gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig", + "Landroid/graphics/Bitmap$Config;"); + gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z"); + gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); + gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); + gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); + + gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor"); + gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I"); + + int ret = AndroidRuntime::registerNativeMethods(env, + "android/graphics/BitmapFactory$Options", + gOptionsMethods, + SK_ARRAY_COUNT(gOptionsMethods)); + if (ret) { + return ret; + } + return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, + gMethods, SK_ARRAY_COUNT(gMethods)); +} diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp new file mode 100644 index 0000000..980003e --- /dev/null +++ b/core/jni/android/graphics/Camera.cpp @@ -0,0 +1,102 @@ +#include "jni.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkCamera.h" + +static jfieldID gNativeInstanceFieldID; + +static void Camera_constructor(JNIEnv* env, jobject obj) { + Sk3DView* view = new Sk3DView; + env->SetIntField(obj, gNativeInstanceFieldID, (int)view); +} + +static void Camera_destructor(JNIEnv* env, jobject obj) { + delete (Sk3DView*)env->GetIntField(obj, gNativeInstanceFieldID); +} + +static void Camera_save(JNIEnv* env, jobject obj) { + Sk3DView* v = (Sk3DView*)env->GetIntField(obj, gNativeInstanceFieldID); + v->save(); +} + +static void Camera_restore(JNIEnv* env, jobject obj) { + Sk3DView* v = (Sk3DView*)env->GetIntField(obj, gNativeInstanceFieldID); + v->restore(); +} + +static void Camera_translate(JNIEnv* env, jobject obj, + float dx, float dy, float dz) { + Sk3DView* v = (Sk3DView*)env->GetIntField(obj, gNativeInstanceFieldID); + v->translate(SkFloatToScalar(dx), SkFloatToScalar(dy), SkFloatToScalar(dz)); +} + +static void Camera_rotateX(JNIEnv* env, jobject obj, float degrees) { + Sk3DView* v = (Sk3DView*)env->GetIntField(obj, gNativeInstanceFieldID); + v->rotateX(SkFloatToScalar(degrees)); +} + +static void Camera_rotateY(JNIEnv* env, jobject obj, float degrees) { + Sk3DView* v = (Sk3DView*)env->GetIntField(obj, gNativeInstanceFieldID); + v->rotateY(SkFloatToScalar(degrees)); +} + +static void Camera_rotateZ(JNIEnv* env, jobject obj, float degrees) { + Sk3DView* v = (Sk3DView*)env->GetIntField(obj, gNativeInstanceFieldID); + v->rotateZ(SkFloatToScalar(degrees)); +} + +static void Camera_getMatrix(JNIEnv* env, jobject obj, int native_matrix) { + Sk3DView* v = (Sk3DView*)env->GetIntField(obj, gNativeInstanceFieldID); + v->getMatrix((SkMatrix*)native_matrix); +} + +static void Camera_applyToCanvas(JNIEnv* env, jobject obj, int native_canvas) { + Sk3DView* v = (Sk3DView*)env->GetIntField(obj, gNativeInstanceFieldID); + v->applyToCanvas((SkCanvas*)native_canvas); +} + +static float Camera_dotWithNormal(JNIEnv* env, jobject obj, + float x, float y, float z) { + Sk3DView* v = (Sk3DView*)env->GetIntField(obj, gNativeInstanceFieldID); + SkScalar dot = v->dotWithNormal(SkFloatToScalar(x), SkFloatToScalar(y), + SkFloatToScalar(z)); + return SkScalarToFloat(dot); +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gCameraMethods[] = { + /* name, signature, funcPtr */ + + { "nativeConstructor", "()V", (void*)Camera_constructor }, + { "nativeDestructor", "()V", (void*)Camera_destructor }, + { "save", "()V", (void*)Camera_save }, + { "restore", "()V", (void*)Camera_restore }, + { "translate", "(FFF)V", (void*)Camera_translate }, + { "rotateX", "(F)V", (void*)Camera_rotateX }, + { "rotateY", "(F)V", (void*)Camera_rotateY }, + { "rotateZ", "(F)V", (void*)Camera_rotateZ }, + { "nativeGetMatrix", "(I)V", (void*)Camera_getMatrix }, + { "nativeApplyToCanvas", "(I)V", (void*)Camera_applyToCanvas }, + { "dotWithNormal", "(FFF)F", (void*)Camera_dotWithNormal } +}; + +int register_android_graphics_Camera(JNIEnv* env); +int register_android_graphics_Camera(JNIEnv* env) { + jclass clazz = env->FindClass("android/graphics/Camera"); + if (clazz == 0) { + return -1; + } + gNativeInstanceFieldID = env->GetFieldID(clazz, "native_instance", "I"); + if (gNativeInstanceFieldID == 0) { + return -1; + } + return android::AndroidRuntime::registerNativeMethods(env, + "android/graphics/Camera", + gCameraMethods, + SK_ARRAY_COUNT(gCameraMethods)); +} + diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp new file mode 100644 index 0000000..841fe49 --- /dev/null +++ b/core/jni/android/graphics/Canvas.cpp @@ -0,0 +1,934 @@ +/* + * Copyright (C) 2006-2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkGLCanvas.h" +#include "SkShader.h" +#include "SkTemplates.h" + +#define TIME_DRAWx + +static uint32_t get_thread_msec() { +#if defined(HAVE_POSIX_CLOCKS) + struct timespec tm; + + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm); + + return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000; +#else + struct timeval tv; + + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000LL + tv.tv_usec / 1000; +#endif +} + +namespace android { + +class SkCanvasGlue { +public: + + static void finalizer(JNIEnv* env, jobject clazz, SkCanvas* canvas) { + canvas->unref(); + } + + static SkCanvas* initRaster(JNIEnv* env, jobject, SkBitmap* bitmap) { + return bitmap ? new SkCanvas(*bitmap) : new SkCanvas; + } + + static SkCanvas* initGL(JNIEnv* env, jobject) { + return new SkGLCanvas; + } + + static void freeGlCaches(JNIEnv* env, jobject) { + SkGLCanvas::DeleteAllTextures(); + } + + static jboolean isOpaque(JNIEnv* env, jobject jcanvas) { + NPE_CHECK_RETURN_ZERO(env, jcanvas); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); + + /* + Currently we cannot support transparency in GL-based canvas' at + the view level. Therefore we cannot base our answer on the device's + bitmap, but need to hard-code the answer. If we relax this + limitation in views, we can simplify the following code as well. + + Use the getViewport() call to find out if we're gl-based... + */ + if (canvas->getViewport(NULL)) { + return true; + } + + // normal technique, rely on the device's bitmap for the answer + return canvas->getDevice()->accessBitmap(false).isOpaque(); + } + + static int getWidth(JNIEnv* env, jobject jcanvas) { + NPE_CHECK_RETURN_ZERO(env, jcanvas); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); + return canvas->getDevice()->accessBitmap(false).width(); + } + + static int getHeight(JNIEnv* env, jobject jcanvas) { + NPE_CHECK_RETURN_ZERO(env, jcanvas); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); + return canvas->getDevice()->accessBitmap(false).height(); + } + + static void setViewport(JNIEnv* env, jobject, SkCanvas* canvas, + int width, int height) { + canvas->setViewport(width, height); + } + + static void setBitmap(JNIEnv* env, jobject, SkCanvas* canvas, + SkBitmap* bitmap) { + canvas->setBitmapDevice(*bitmap); + } + + static int saveAll(JNIEnv* env, jobject jcanvas) { + NPE_CHECK_RETURN_ZERO(env, jcanvas); + return GraphicsJNI::getNativeCanvas(env, jcanvas)->save(); + } + + static int save(JNIEnv* env, jobject jcanvas, SkCanvas::SaveFlags flags) { + NPE_CHECK_RETURN_ZERO(env, jcanvas); + return GraphicsJNI::getNativeCanvas(env, jcanvas)->save(flags); + } + + static int saveLayer(JNIEnv* env, jobject, SkCanvas* canvas, jobject bounds, + SkPaint* paint, int flags) { + SkRect* bounds_ = NULL; + SkRect storage; + if (bounds != NULL) { + GraphicsJNI::jrectf_to_rect(env, bounds, &storage); + bounds_ = &storage; + } + return canvas->saveLayer(bounds_, paint, (SkCanvas::SaveFlags)flags); + } + + static int saveLayer4F(JNIEnv* env, jobject, SkCanvas* canvas, + jfloat l, jfloat t, jfloat r, jfloat b, + SkPaint* paint, int flags) { + SkRect bounds; + bounds.set(SkFloatToScalar(l), SkFloatToScalar(t), SkFloatToScalar(r), + SkFloatToScalar(b)); + return canvas->saveLayer(&bounds, paint, (SkCanvas::SaveFlags)flags); + } + + static int saveLayerAlpha(JNIEnv* env, jobject, SkCanvas* canvas, + jobject bounds, int alpha, int flags) { + SkRect* bounds_ = NULL; + SkRect storage; + if (bounds != NULL) { + GraphicsJNI::jrectf_to_rect(env, bounds, &storage); + bounds_ = &storage; + } + return canvas->saveLayerAlpha(bounds_, alpha, + (SkCanvas::SaveFlags)flags); + } + + static int saveLayerAlpha4F(JNIEnv* env, jobject, SkCanvas* canvas, + jfloat l, jfloat t, jfloat r, jfloat b, + int alpha, int flags) { + SkRect bounds; + bounds.set(SkFloatToScalar(l), SkFloatToScalar(t), SkFloatToScalar(r), + SkFloatToScalar(b)); + return canvas->saveLayerAlpha(&bounds, alpha, + (SkCanvas::SaveFlags)flags); + } + + static void restore(JNIEnv* env, jobject jcanvas) { + NPE_CHECK_RETURN_VOID(env, jcanvas); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); + if (canvas->getSaveCount() <= 1) { // cannot restore anymore + doThrowISE(env, "Underflow in restore"); + return; + } + canvas->restore(); + } + + static int getSaveCount(JNIEnv* env, jobject jcanvas) { + NPE_CHECK_RETURN_ZERO(env, jcanvas); + return GraphicsJNI::getNativeCanvas(env, jcanvas)->getSaveCount(); + } + + static void restoreToCount(JNIEnv* env, jobject jcanvas, int restoreCount) { + NPE_CHECK_RETURN_VOID(env, jcanvas); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); + if (restoreCount < 1) { + doThrowIAE(env, "Underflow in restoreToCount"); + return; + } + canvas->restoreToCount(restoreCount); + } + + static void translate(JNIEnv* env, jobject jcanvas, jfloat dx, jfloat dy) { + NPE_CHECK_RETURN_VOID(env, jcanvas); + SkScalar dx_ = SkFloatToScalar(dx); + SkScalar dy_ = SkFloatToScalar(dy); + (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->translate(dx_, dy_); + } + + static void scale__FF(JNIEnv* env, jobject jcanvas, jfloat sx, jfloat sy) { + NPE_CHECK_RETURN_VOID(env, jcanvas); + SkScalar sx_ = SkFloatToScalar(sx); + SkScalar sy_ = SkFloatToScalar(sy); + (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->scale(sx_, sy_); + } + + static void rotate__F(JNIEnv* env, jobject jcanvas, jfloat degrees) { + NPE_CHECK_RETURN_VOID(env, jcanvas); + SkScalar degrees_ = SkFloatToScalar(degrees); + (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->rotate(degrees_); + } + + static void skew__FF(JNIEnv* env, jobject jcanvas, jfloat sx, jfloat sy) { + NPE_CHECK_RETURN_VOID(env, jcanvas); + SkScalar sx_ = SkFloatToScalar(sx); + SkScalar sy_ = SkFloatToScalar(sy); + (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->skew(sx_, sy_); + } + + static void concat(JNIEnv* env, jobject, SkCanvas* canvas, + const SkMatrix* matrix) { + canvas->concat(*matrix); + } + + static void setMatrix(JNIEnv* env, jobject, SkCanvas* canvas, + const SkMatrix* matrix) { + if (NULL == matrix) { + canvas->resetMatrix(); + } else { + canvas->setMatrix(*matrix); + } + } + + static jboolean clipRect_FFFF(JNIEnv* env, jobject jcanvas, jfloat left, + jfloat top, jfloat right, jfloat bottom) { + NPE_CHECK_RETURN_ZERO(env, jcanvas); + SkRect r; + r.set(SkFloatToScalar(left), SkFloatToScalar(top), + SkFloatToScalar(right), SkFloatToScalar(bottom)); + SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas); + return c->clipRect(r); + } + + static jboolean clipRect_IIII(JNIEnv* env, jobject jcanvas, jint left, + jint top, jint right, jint bottom) { + NPE_CHECK_RETURN_ZERO(env, jcanvas); + SkRect r; + r.set(SkIntToScalar(left), SkIntToScalar(top), + SkIntToScalar(right), SkIntToScalar(bottom)); + return GraphicsJNI::getNativeCanvas(env, jcanvas)->clipRect(r); + } + + static jboolean clipRect_RectF(JNIEnv* env, jobject jcanvas, jobject rectf) { + NPE_CHECK_RETURN_ZERO(env, jcanvas); + NPE_CHECK_RETURN_ZERO(env, rectf); + SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas); + SkRect tmp; + return c->clipRect(*GraphicsJNI::jrectf_to_rect(env, rectf, &tmp)); + } + + static jboolean clipRect_Rect(JNIEnv* env, jobject jcanvas, jobject rect) { + NPE_CHECK_RETURN_ZERO(env, jcanvas); + NPE_CHECK_RETURN_ZERO(env, rect); + SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas); + SkRect tmp; + return c->clipRect(*GraphicsJNI::jrect_to_rect(env, rect, &tmp)); + } + + static jboolean clipRect(JNIEnv* env, jobject, SkCanvas* canvas, + float left, float top, float right, float bottom, + int op) { + SkRect rect; + rect.set(SkFloatToScalar(left), SkFloatToScalar(top), + SkFloatToScalar(right), SkFloatToScalar(bottom)); + return canvas->clipRect(rect, (SkRegion::Op)op); + } + + static jboolean clipPath(JNIEnv* env, jobject, SkCanvas* canvas, + SkPath* path, int op) { + return canvas->clipPath(*path, (SkRegion::Op)op); + } + + static jboolean clipRegion(JNIEnv* env, jobject, SkCanvas* canvas, + SkRegion* deviceRgn, int op) { + return canvas->clipRegion(*deviceRgn, (SkRegion::Op)op); + } + + static void setDrawFilter(JNIEnv* env, jobject, SkCanvas* canvas, + SkDrawFilter* filter) { + canvas->setDrawFilter(filter); + } + + static jboolean quickReject__RectFI(JNIEnv* env, jobject, SkCanvas* canvas, + jobject rect, int edgetype) { + SkRect rect_; + GraphicsJNI::jrectf_to_rect(env, rect, &rect_); + return canvas->quickReject(rect_, (SkCanvas::EdgeType)edgetype); + } + + static jboolean quickReject__PathI(JNIEnv* env, jobject, SkCanvas* canvas, + SkPath* path, int edgetype) { + return canvas->quickReject(*path, (SkCanvas::EdgeType)edgetype); + } + + static jboolean quickReject__FFFFI(JNIEnv* env, jobject, SkCanvas* canvas, + jfloat left, jfloat top, jfloat right, + jfloat bottom, int edgetype) { + SkRect r; + r.set(SkFloatToScalar(left), SkFloatToScalar(top), + SkFloatToScalar(right), SkFloatToScalar(bottom)); + return canvas->quickReject(r, (SkCanvas::EdgeType)edgetype); + } + + static void drawRGB(JNIEnv* env, jobject, SkCanvas* canvas, + jint r, jint g, jint b) { + canvas->drawARGB(0xFF, r, g, b); + } + + static void drawARGB(JNIEnv* env, jobject, SkCanvas* canvas, + jint a, jint r, jint g, jint b) { + canvas->drawARGB(a, r, g, b); + } + + static void drawColor__I(JNIEnv* env, jobject, SkCanvas* canvas, + jint color) { + canvas->drawColor(color); + } + + static void drawColor__II(JNIEnv* env, jobject, SkCanvas* canvas, + jint color, SkPorterDuff::Mode mode) { + canvas->drawColor(color, mode); + } + + static void drawPaint(JNIEnv* env, jobject, SkCanvas* canvas, + SkPaint* paint) { + canvas->drawPaint(*paint); + } + + static void doPoints(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray, + jint offset, jint count, jobject jpaint, + SkCanvas::PointMode mode) { + NPE_CHECK_RETURN_VOID(env, jcanvas); + NPE_CHECK_RETURN_VOID(env, jptsArray); + NPE_CHECK_RETURN_VOID(env, jpaint); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); + const SkPaint& paint = *GraphicsJNI::getNativePaint(env, jpaint); + + AutoJavaFloatArray autoPts(env, jptsArray); + float* floats = autoPts.ptr(); + const int length = autoPts.length(); + + if ((offset | count) < 0 || offset + count > length) { + doThrowAIOOBE(env); + return; + } + + // now convert the floats into SkPoints + count >>= 1; // now it is the number of points + SkAutoSTMalloc<32, SkPoint> storage(count); + SkPoint* pts = storage.get(); + const float* src = floats + offset; + for (int i = 0; i < count; i++) { + pts[i].set(SkFloatToScalar(src[0]), SkFloatToScalar(src[1])); + src += 2; + } + canvas->drawPoints(mode, count, pts, paint); + } + + static void drawPoints(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray, + jint offset, jint count, jobject jpaint) { + doPoints(env, jcanvas, jptsArray, offset, count, jpaint, + SkCanvas::kPoints_PointMode); + } + + static void drawLines(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray, + jint offset, jint count, jobject jpaint) { + doPoints(env, jcanvas, jptsArray, offset, count, jpaint, + SkCanvas::kLines_PointMode); + } + + static void drawPoint(JNIEnv* env, jobject jcanvas, float x, float y, + jobject jpaint) { + NPE_CHECK_RETURN_VOID(env, jcanvas); + NPE_CHECK_RETURN_VOID(env, jpaint); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); + const SkPaint& paint = *GraphicsJNI::getNativePaint(env, jpaint); + + canvas->drawPoint(SkFloatToScalar(x), SkFloatToScalar(y), paint); + } + + static void drawLine__FFFFPaint(JNIEnv* env, jobject, SkCanvas* canvas, + jfloat startX, jfloat startY, jfloat stopX, + jfloat stopY, SkPaint* paint) { + canvas->drawLine(SkFloatToScalar(startX), SkFloatToScalar(startY), + SkFloatToScalar(stopX), SkFloatToScalar(stopY), + *paint); + } + + static void drawRect__RectFPaint(JNIEnv* env, jobject, SkCanvas* canvas, + jobject rect, SkPaint* paint) { + SkRect rect_; + GraphicsJNI::jrectf_to_rect(env, rect, &rect_); + canvas->drawRect(rect_, *paint); + } + + static void drawRect__FFFFPaint(JNIEnv* env, jobject, SkCanvas* canvas, + jfloat left, jfloat top, jfloat right, + jfloat bottom, SkPaint* paint) { + SkScalar left_ = SkFloatToScalar(left); + SkScalar top_ = SkFloatToScalar(top); + SkScalar right_ = SkFloatToScalar(right); + SkScalar bottom_ = SkFloatToScalar(bottom); + canvas->drawRectCoords(left_, top_, right_, bottom_, *paint); + } + + static void drawOval(JNIEnv* env, jobject, SkCanvas* canvas, jobject joval, + SkPaint* paint) { + SkRect oval; + GraphicsJNI::jrectf_to_rect(env, joval, &oval); + canvas->drawOval(oval, *paint); + } + + static void drawCircle(JNIEnv* env, jobject, SkCanvas* canvas, jfloat cx, + jfloat cy, jfloat radius, SkPaint* paint) { + canvas->drawCircle(SkFloatToScalar(cx), SkFloatToScalar(cy), + SkFloatToScalar(radius), *paint); + } + + static void drawArc(JNIEnv* env, jobject, SkCanvas* canvas, jobject joval, + jfloat startAngle, jfloat sweepAngle, + jboolean useCenter, SkPaint* paint) { + SkRect oval; + GraphicsJNI::jrectf_to_rect(env, joval, &oval); + canvas->drawArc(oval, SkFloatToScalar(startAngle), + SkFloatToScalar(sweepAngle), useCenter, *paint); + } + + static void drawRoundRect(JNIEnv* env, jobject, SkCanvas* canvas, + jobject jrect, jfloat rx, jfloat ry, + SkPaint* paint) { + SkRect rect; + GraphicsJNI::jrectf_to_rect(env, jrect, &rect); + canvas->drawRoundRect(rect, SkFloatToScalar(rx), SkFloatToScalar(ry), + *paint); + } + + static void drawPath(JNIEnv* env, jobject, SkCanvas* canvas, SkPath* path, + SkPaint* paint) { + canvas->drawPath(*path, *paint); + } + + static void drawPicture(JNIEnv* env, jobject, SkCanvas* canvas, + SkPicture* picture) { + SkASSERT(canvas); + SkASSERT(picture); + +#ifdef TIME_DRAW + SkMSec now = get_thread_msec(); //SkTime::GetMSecs(); +#endif + canvas->drawPicture(*picture); +#ifdef TIME_DRAW + LOGD("---- picture playback %d ms\n", get_thread_msec() - now); +#endif + } + + static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject, + SkCanvas* canvas, SkBitmap* bitmap, + jfloat left, jfloat top, + SkPaint* paint) { + SkScalar left_ = SkFloatToScalar(left); + SkScalar top_ = SkFloatToScalar(top); + canvas->drawBitmap(*bitmap, left_, top_, paint); + } + + static void doDrawBitmap(JNIEnv* env, SkCanvas* canvas, SkBitmap* bitmap, + jobject srcIRect, const SkRect& dst, SkPaint* paint) { + SkIRect src, *srcPtr = NULL; + + if (NULL != srcIRect) { + GraphicsJNI::jrect_to_irect(env, srcIRect, &src); + srcPtr = &src; + } + canvas->drawBitmapRect(*bitmap, srcPtr, dst, paint); + } + + static void drawBitmapRF(JNIEnv* env, jobject, SkCanvas* canvas, + SkBitmap* bitmap, jobject srcIRect, + jobject dstRectF, SkPaint* paint) { + SkRect dst; + GraphicsJNI::jrectf_to_rect(env, dstRectF, &dst); + doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint); + } + + static void drawBitmapRR(JNIEnv* env, jobject, SkCanvas* canvas, + SkBitmap* bitmap, jobject srcIRect, + jobject dstRect, SkPaint* paint) { + SkRect dst; + GraphicsJNI::jrect_to_rect(env, dstRect, &dst); + doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint); + } + + static void drawBitmapArray(JNIEnv* env, jobject, SkCanvas* canvas, + jintArray jcolors, int offset, int stride, + int x, int y, int width, int height, + jboolean hasAlpha, SkPaint* paint) + { + SkBitmap bitmap; + + bitmap.setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config : + SkBitmap::kRGB_565_Config, width, height); + if (!bitmap.allocPixels()) { + return; + } + + if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, + 0, 0, width, height, bitmap)) { + return; + } + + canvas->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint); + } + + static void drawBitmapMatrix(JNIEnv* env, jobject, SkCanvas* canvas, + const SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint) { + canvas->drawBitmapMatrix(*bitmap, *matrix, paint); + } + + static void drawBitmapMesh(JNIEnv* env, jobject, SkCanvas* canvas, + const SkBitmap* bitmap, int meshWidth, int meshHeight, + jfloatArray jverts, int vertIndex, jintArray jcolors, + int colorIndex, const SkPaint* paint) { + + const int ptCount = (meshWidth + 1) * (meshHeight + 1); + const int indexCount = meshWidth * meshHeight * 6; + + AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1)); + AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount); + + /* Our temp storage holds 2 or 3 arrays. + texture points [ptCount * sizeof(SkPoint)] + optionally vertex points [ptCount * sizeof(SkPoint)] if we need a + copy to convert from float to fixed + indices [ptCount * sizeof(uint16_t)] + */ + ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[] +#ifdef SK_SCALAR_IS_FIXED + storageSize += ptCount * sizeof(SkPoint); // storage for verts +#endif + storageSize += indexCount * sizeof(uint16_t); // indices[] + + SkAutoMalloc storage(storageSize); + SkPoint* texs = (SkPoint*)storage.get(); + SkPoint* verts; + uint16_t* indices; +#ifdef SK_SCALAR_IS_FLOAT + verts = (SkPoint*)(vertA.ptr() + vertIndex); + indices = (uint16_t*)(texs + ptCount); +#else + verts = texs + ptCount; + indices = (uint16_t*)(verts + ptCount); + // convert floats to fixed + { + const float* src = vertA.ptr() + vertIndex; + for (int i = 0; i < ptCount; i++) { + verts[i].set(SkFloatToFixed(src[0]), SkFloatToFixed(src[1])); + src += 2; + } + } +#endif + + // cons up texture coordinates and indices + { + const SkScalar w = SkIntToScalar(bitmap->width()); + const SkScalar h = SkIntToScalar(bitmap->height()); + const SkScalar dx = w / meshWidth; + const SkScalar dy = h / meshHeight; + + SkPoint* texsPtr = texs; + SkScalar y = 0; + for (int i = 0; i <= meshHeight; i++) { + if (i == meshHeight) { + y = h; // to ensure numerically we hit h exactly + } + SkScalar x = 0; + for (int j = 0; j < meshWidth; j++) { + texsPtr->set(x, y); + texsPtr += 1; + x += dx; + } + texsPtr->set(w, y); + texsPtr += 1; + y += dy; + } + SkASSERT(texsPtr - texs == ptCount); + } + + // cons up indices + { + uint16_t* indexPtr = indices; + int index = 0; + for (int i = 0; i < meshHeight; i++) { + for (int j = 0; j < meshWidth; j++) { + // lower-left triangle + *indexPtr++ = index; + *indexPtr++ = index + meshWidth + 1; + *indexPtr++ = index + meshWidth + 2; + // upper-right triangle + *indexPtr++ = index; + *indexPtr++ = index + meshWidth + 2; + *indexPtr++ = index + 1; + // bump to the next cell + index += 1; + } + // bump to the next row + index += 1; + } + SkASSERT(indexPtr - indices == indexCount); + SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize); + } + + // double-check that we have legal indices +#ifdef SK_DEBUG + { + for (int i = 0; i < indexCount; i++) { + SkASSERT((unsigned)indices[i] < (unsigned)ptCount); + } + } +#endif + + // cons-up a shader for the bitmap + SkPaint tmpPaint; + if (paint) { + tmpPaint = *paint; + } + SkShader* shader = SkShader::CreateBitmapShader(*bitmap, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); + tmpPaint.setShader(shader)->safeUnref(); + + canvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, verts, + texs, (const SkColor*)colorA.ptr(), NULL, indices, + indexCount, tmpPaint); + } + + static void drawVertices(JNIEnv* env, jobject, SkCanvas* canvas, + SkCanvas::VertexMode mode, int vertexCount, + jfloatArray jverts, int vertIndex, + jfloatArray jtexs, int texIndex, + jintArray jcolors, int colorIndex, + jshortArray jindices, int indexIndex, + int indexCount, const SkPaint* paint) { + + AutoJavaFloatArray vertA(env, jverts, vertIndex + vertexCount); + AutoJavaFloatArray texA(env, jtexs, texIndex + vertexCount); + AutoJavaIntArray colorA(env, jcolors, colorIndex + vertexCount); + AutoJavaShortArray indexA(env, jindices, indexIndex + indexCount); + + const int ptCount = vertexCount >> 1; + + SkPoint* verts; + SkPoint* texs = NULL; +#ifdef SK_SCALAR_IS_FLOAT + verts = (SkPoint*)(vertA.ptr() + vertIndex); + if (jtexs != NULL) { + texs = (SkPoint*)(texA.ptr() + texIndex); + } +#else + int count = ptCount; // for verts + if (jtexs != NULL) { + count += ptCount; // += for texs + } + SkAutoMalloc storage(count * sizeof(SkPoint)); + verts = (SkPoint*)storage.get(); + const float* src = vertA.ptr() + vertIndex; + for (int i = 0; i < ptCount; i++) { + verts[i].set(SkFloatToFixed(src[0]), SkFloatToFixed(src[1])); + src += 2; + } + if (jtexs != NULL) { + texs = verts + ptCount; + src = texA.ptr() + texIndex; + for (int i = 0; i < ptCount; i++) { + texs[i].set(SkFloatToFixed(src[0]), SkFloatToFixed(src[1])); + src += 2; + } + } +#endif + + const SkColor* colors = NULL; + const uint16_t* indices = NULL; + if (jcolors != NULL) { + colors = (const SkColor*)(colorA.ptr() + colorIndex); + } + if (jindices != NULL) { + indices = (const uint16_t*)(indexA.ptr() + indexIndex); + } + + canvas->drawVertices(mode, ptCount, verts, texs, colors, NULL, + indices, indexCount, *paint); + } + + static void drawText___CIIFFPaint(JNIEnv* env, jobject, SkCanvas* canvas, + jcharArray text, int index, int count, + jfloat x, jfloat y, SkPaint* paint) { + jchar* textArray = env->GetCharArrayElements(text, NULL); + jsize textCount = env->GetArrayLength(text); + SkScalar x_ = SkFloatToScalar(x); + SkScalar y_ = SkFloatToScalar(y); + textArray += index; + canvas->drawText(textArray, count << 1, x_, y_, *paint); + env->ReleaseCharArrayElements(text, textArray, 0); + } + + static void drawText__StringIIFFPaint(JNIEnv* env, jobject, + SkCanvas* canvas, jstring text, int start, int end, + jfloat x, jfloat y, SkPaint* paint) { + const void* text_ = env->GetStringChars(text, NULL); + SkScalar x_ = SkFloatToScalar(x); + SkScalar y_ = SkFloatToScalar(y); + canvas->drawText((const uint16_t*)text_ + start, (end - start) << 1, + x_, y_, *paint); + env->ReleaseStringChars(text, (const jchar*) text_); + } + + static void drawString(JNIEnv* env, jobject canvas, jstring text, + jfloat x, jfloat y, jobject paint) { + NPE_CHECK_RETURN_VOID(env, canvas); + NPE_CHECK_RETURN_VOID(env, paint); + NPE_CHECK_RETURN_VOID(env, text); + size_t count = env->GetStringLength(text); + if (0 == count) { + return; + } + const jchar* text_ = env->GetStringChars(text, NULL); + SkCanvas* c = GraphicsJNI::getNativeCanvas(env, canvas); + c->drawText(text_, count << 1, SkFloatToScalar(x), SkFloatToScalar(y), + *GraphicsJNI::getNativePaint(env, paint)); + env->ReleaseStringChars(text, text_); + } + + static void drawPosText___CII_FPaint(JNIEnv* env, jobject, SkCanvas* canvas, + jcharArray text, int index, int count, + jfloatArray pos, SkPaint* paint) { + jchar* textArray = text ? env->GetCharArrayElements(text, NULL) : NULL; + jsize textCount = text ? env->GetArrayLength(text) : NULL; + float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL; + int posCount = pos ? env->GetArrayLength(pos) >> 1: 0; + SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL; + int indx; + for (indx = 0; indx < posCount; indx++) { + posPtr[indx].fX = SkFloatToScalar(posArray[indx << 1]); + posPtr[indx].fY = SkFloatToScalar(posArray[(indx << 1) + 1]); + } + textArray += index; + canvas->drawPosText(textArray, count << 1, posPtr, *paint); + if (text) { + env->ReleaseCharArrayElements(text, textArray, 0); + } + if (pos) { + env->ReleaseFloatArrayElements(pos, posArray, 0); + } + delete[] posPtr; + } + + static void drawPosText__String_FPaint(JNIEnv* env, jobject, + SkCanvas* canvas, jstring text, + jfloatArray pos, SkPaint* paint) { + const void* text_ = text ? env->GetStringChars(text, NULL) : NULL; + int byteLength = text ? env->GetStringLength(text) : 0; + float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL; + int posCount = pos ? env->GetArrayLength(pos) >> 1: 0; + SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL; + + for (int indx = 0; indx < posCount; indx++) { + posPtr[indx].fX = SkFloatToScalar(posArray[indx << 1]); + posPtr[indx].fY = SkFloatToScalar(posArray[(indx << 1) + 1]); + } + canvas->drawPosText(text_, byteLength << 1, posPtr, *paint); + if (text) { + env->ReleaseStringChars(text, (const jchar*) text_); + } + if (pos) { + env->ReleaseFloatArrayElements(pos, posArray, 0); + } + delete[] posPtr; + } + + static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject, + SkCanvas* canvas, jcharArray text, int index, int count, + SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) { + + jchar* textArray = env->GetCharArrayElements(text, NULL); + canvas->drawTextOnPathHV(textArray + index, count << 1, *path, + SkFloatToScalar(hOffset), SkFloatToScalar(vOffset), *paint); + env->ReleaseCharArrayElements(text, textArray, 0); + } + + static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject, + SkCanvas* canvas, jstring text, SkPath* path, + jfloat hOffset, jfloat vOffset, SkPaint* paint) { + const jchar* text_ = env->GetStringChars(text, NULL); + int byteLength = env->GetStringLength(text) << 1; + canvas->drawTextOnPathHV(text_, byteLength, *path, + SkFloatToScalar(hOffset), SkFloatToScalar(vOffset), *paint); + env->ReleaseStringChars(text, text_); + } + + static bool getClipBounds(JNIEnv* env, jobject, SkCanvas* canvas, + jobject bounds) { + SkRect r; + SkIRect ir; + bool result = canvas->getClipBounds(&r); + + r.round(&ir); + (void)GraphicsJNI::irect_to_jrect(ir, env, bounds); + return result; + } + + static void getCTM(JNIEnv* env, jobject, SkCanvas* canvas, + SkMatrix* matrix) { + *matrix = canvas->getTotalMatrix(); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static JNINativeMethod gCanvasMethods[] = { + {"finalizer", "(I)V", (void*) SkCanvasGlue::finalizer}, + {"initRaster","(I)I", (void*) SkCanvasGlue::initRaster}, + {"initGL","()I", (void*) SkCanvasGlue::initGL}, + {"isOpaque","()Z", (void*) SkCanvasGlue::isOpaque}, + {"getWidth","()I", (void*) SkCanvasGlue::getWidth}, + {"getHeight","()I", (void*) SkCanvasGlue::getHeight}, + {"native_setBitmap","(II)V", (void*) SkCanvasGlue::setBitmap}, + {"nativeSetViewport", "(III)V", (void*) SkCanvasGlue::setViewport}, + {"save","()I", (void*) SkCanvasGlue::saveAll}, + {"save","(I)I", (void*) SkCanvasGlue::save}, + {"native_saveLayer","(ILandroid/graphics/RectF;II)I", + (void*) SkCanvasGlue::saveLayer}, + {"native_saveLayer","(IFFFFII)I", (void*) SkCanvasGlue::saveLayer4F}, + {"native_saveLayerAlpha","(ILandroid/graphics/RectF;II)I", + (void*) SkCanvasGlue::saveLayerAlpha}, + {"native_saveLayerAlpha","(IFFFFII)I", + (void*) SkCanvasGlue::saveLayerAlpha4F}, + {"restore","()V", (void*) SkCanvasGlue::restore}, + {"getSaveCount","()I", (void*) SkCanvasGlue::getSaveCount}, + {"restoreToCount","(I)V", (void*) SkCanvasGlue::restoreToCount}, + {"translate","(FF)V", (void*) SkCanvasGlue::translate}, + {"scale","(FF)V", (void*) SkCanvasGlue::scale__FF}, + {"rotate","(F)V", (void*) SkCanvasGlue::rotate__F}, + {"skew","(FF)V", (void*) SkCanvasGlue::skew__FF}, + {"native_concat","(II)V", (void*) SkCanvasGlue::concat}, + {"native_setMatrix","(II)V", (void*) SkCanvasGlue::setMatrix}, + {"clipRect","(FFFF)Z", (void*) SkCanvasGlue::clipRect_FFFF}, + {"clipRect","(IIII)Z", (void*) SkCanvasGlue::clipRect_IIII}, + {"clipRect","(Landroid/graphics/RectF;)Z", + (void*) SkCanvasGlue::clipRect_RectF}, + {"clipRect","(Landroid/graphics/Rect;)Z", + (void*) SkCanvasGlue::clipRect_Rect}, + {"native_clipRect","(IFFFFI)Z", (void*) SkCanvasGlue::clipRect}, + {"native_clipPath","(III)Z", (void*) SkCanvasGlue::clipPath}, + {"native_clipRegion","(III)Z", (void*) SkCanvasGlue::clipRegion}, + {"nativeSetDrawFilter", "(II)V", (void*) SkCanvasGlue::setDrawFilter}, + {"native_getClipBounds","(ILandroid/graphics/Rect;)Z", + (void*) SkCanvasGlue::getClipBounds}, + {"native_getCTM", "(II)V", (void*)SkCanvasGlue::getCTM}, + {"native_quickReject","(ILandroid/graphics/RectF;I)Z", + (void*) SkCanvasGlue::quickReject__RectFI}, + {"native_quickReject","(III)Z", (void*) SkCanvasGlue::quickReject__PathI}, + {"native_quickReject","(IFFFFI)Z", (void*)SkCanvasGlue::quickReject__FFFFI}, + {"native_drawRGB","(IIII)V", (void*) SkCanvasGlue::drawRGB}, + {"native_drawARGB","(IIIII)V", (void*) SkCanvasGlue::drawARGB}, + {"native_drawColor","(II)V", (void*) SkCanvasGlue::drawColor__I}, + {"native_drawColor","(III)V", (void*) SkCanvasGlue::drawColor__II}, + {"native_drawPaint","(II)V", (void*) SkCanvasGlue::drawPaint}, + {"drawPoint", "(FFLandroid/graphics/Paint;)V", + (void*) SkCanvasGlue::drawPoint}, + {"drawPoints", "([FIILandroid/graphics/Paint;)V", + (void*) SkCanvasGlue::drawPoints}, + {"drawLines", "([FIILandroid/graphics/Paint;)V", + (void*) SkCanvasGlue::drawLines}, + {"native_drawLine","(IFFFFI)V", (void*) SkCanvasGlue::drawLine__FFFFPaint}, + {"native_drawRect","(ILandroid/graphics/RectF;I)V", + (void*) SkCanvasGlue::drawRect__RectFPaint}, + {"native_drawRect","(IFFFFI)V", (void*) SkCanvasGlue::drawRect__FFFFPaint}, + {"native_drawOval","(ILandroid/graphics/RectF;I)V", + (void*) SkCanvasGlue::drawOval}, + {"native_drawCircle","(IFFFI)V", (void*) SkCanvasGlue::drawCircle}, + {"native_drawArc","(ILandroid/graphics/RectF;FFZI)V", + (void*) SkCanvasGlue::drawArc}, + {"native_drawRoundRect","(ILandroid/graphics/RectF;FFI)V", + (void*) SkCanvasGlue::drawRoundRect}, + {"native_drawPath","(III)V", (void*) SkCanvasGlue::drawPath}, + {"native_drawBitmap","(IIFFI)V", + (void*) SkCanvasGlue::drawBitmap__BitmapFFPaint}, + {"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/RectF;I)V", + (void*) SkCanvasGlue::drawBitmapRF}, + {"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/Rect;I)V", + (void*) SkCanvasGlue::drawBitmapRR}, + {"native_drawBitmap", "(I[IIIIIIIZI)V", + (void*)SkCanvasGlue::drawBitmapArray}, + + {"nativeDrawBitmapMatrix", "(IIII)V", + (void*)SkCanvasGlue::drawBitmapMatrix}, + {"nativeDrawBitmapMesh", "(IIII[FI[III)V", + (void*)SkCanvasGlue::drawBitmapMesh}, + {"nativeDrawVertices", "(III[FI[FI[II[SIII)V", + (void*)SkCanvasGlue::drawVertices}, + {"native_drawText","(I[CIIFFI)V", + (void*) SkCanvasGlue::drawText___CIIFFPaint}, + {"native_drawText","(ILjava/lang/String;IIFFI)V", + (void*) SkCanvasGlue::drawText__StringIIFFPaint}, + {"drawText","(Ljava/lang/String;FFLandroid/graphics/Paint;)V", + (void*) SkCanvasGlue::drawString}, + {"native_drawPosText","(I[CII[FI)V", + (void*) SkCanvasGlue::drawPosText___CII_FPaint}, + {"native_drawPosText","(ILjava/lang/String;[FI)V", + (void*) SkCanvasGlue::drawPosText__String_FPaint}, + {"native_drawTextOnPath","(I[CIIIFFI)V", + (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint}, + {"native_drawTextOnPath","(ILjava/lang/String;IFFI)V", + (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint}, + {"native_drawPicture", "(II)V", (void*) SkCanvasGlue::drawPicture}, + + {"freeGlCaches", "()V", (void*) SkCanvasGlue::freeGlCaches} +}; + +#include <android_runtime/AndroidRuntime.h> + +#define REG(env, name, array) \ + result = android::AndroidRuntime::registerNativeMethods(env, name, array, \ + SK_ARRAY_COUNT(array)); \ + if (result < 0) return result + +int register_android_graphics_Canvas(JNIEnv* env) { + int result; + + REG(env, "android/graphics/Canvas", gCanvasMethods); + + return result; +} + +} diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp new file mode 100644 index 0000000..b6ec4a2 --- /dev/null +++ b/core/jni/android/graphics/ColorFilter.cpp @@ -0,0 +1,98 @@ +/* libs/android_runtime/android/graphics/ColorFilter.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkColorFilter.h" +#include "SkColorMatrixFilter.h" + +namespace android { + +class SkColorFilterGlue { +public: + + static void finalizer(JNIEnv* env, jobject clazz, SkColorFilter* obj) { + obj->safeUnref(); + } + + static SkColorFilter* CreatePorterDuffFilter(JNIEnv* env, jobject, + jint srcColor, SkPorterDuff::Mode porterDuffMode) { + return SkColorFilter::CreatePorterDuffFilter(srcColor, porterDuffMode); + } + + static SkColorFilter* CreateLightingFilter(JNIEnv* env, jobject, + jint mul, jint add) { + return SkColorFilter::CreateLightingFilter(mul, add); + } + + static SkColorFilter* CreateColorMatrixFilter(JNIEnv* env, jobject, + jfloatArray jarray) { + AutoJavaFloatArray autoArray(env, jarray, 20); + const float* src = autoArray.ptr(); + +#ifdef SK_SCALAR_IS_FIXED + SkFixed array[20]; + for (int i = 0; i < 20; i++) { + array[i] = SkFloatToScalar(src[i]); + } + return new SkColorMatrixFilter(array); +#else + return new SkColorMatrixFilter(src); +#endif + } + +}; + +static JNINativeMethod colorfilter_methods[] = { + {"finalizer", "(I)V", (void*) SkColorFilterGlue::finalizer} +}; + +static JNINativeMethod porterduff_methods[] = { + {"native_CreatePorterDuffFilter","(II)I", + (void*) SkColorFilterGlue::CreatePorterDuffFilter} +}; + +static JNINativeMethod lighting_methods[] = { + {"native_CreateLightingFilter","(II)I", + (void*) SkColorFilterGlue::CreateLightingFilter} +}; + +static JNINativeMethod colormatrix_methods[] = { + {"nativeColorMatrixFilter","([F)I", + (void*) SkColorFilterGlue::CreateColorMatrixFilter} +}; + +#define REG(env, name, array) \ + result = android::AndroidRuntime::registerNativeMethods(env, name, array, \ + SK_ARRAY_COUNT(array)); \ + if (result < 0) return result + + +int register_android_graphics_ColorFilter(JNIEnv* env) { + int result; + + REG(env, "android/graphics/ColorFilter", colorfilter_methods); + REG(env, "android/graphics/PorterDuffColorFilter", porterduff_methods); + REG(env, "android/graphics/LightingColorFilter", lighting_methods); + REG(env, "android/graphics/ColorMatrixColorFilter", colormatrix_methods); + + return 0; +} + +} diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp new file mode 100644 index 0000000..002fdf9 --- /dev/null +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp @@ -0,0 +1,212 @@ +#include "CreateJavaOutputStreamAdaptor.h" + +#define RETURN_NULL_IF_NULL(value) \ + do { if (!(value)) { SkASSERT(0); return NULL; } } while (false) + +static jclass gInputStream_Clazz; +static jmethodID gInputStream_resetMethodID; +static jmethodID gInputStream_availableMethodID; +static jmethodID gInputStream_readMethodID; +static jmethodID gInputStream_skipMethodID; + +class JavaInputStreamAdaptor : public SkStream { +public: + JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar) + : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) { + SkASSERT(ar); + fCapacity = env->GetArrayLength(ar); + SkASSERT(fCapacity > 0); + fBytesRead = 0; + } + + virtual bool rewind() { + JNIEnv* env = fEnv; + + fBytesRead = 0; + + env->CallVoidMethod(fJavaInputStream, gInputStream_resetMethodID); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + printf("------- reset threw an exception\n"); + return false; + } + return true; + } + + virtual size_t read(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); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + printf("---- read threw an exception\n"); + return 0; + } + + if (n <= 0) { + break; // eof + } + + jbyte* array = env->GetByteArrayElements(fJavaByteArray, NULL); + memcpy(buffer, array, n); + env->ReleaseByteArrayElements(fJavaByteArray, array, 0); + + buffer = (void*)((char*)buffer + n); + bytesRead += n; + size -= n; + fBytesRead += n; + } while (size != 0); + + return bytesRead; + } + +private: + JNIEnv* fEnv; + jobject fJavaInputStream; // the caller owns this object + jbyteArray fJavaByteArray; // the caller owns this object + size_t fCapacity; + size_t fBytesRead; +}; + +SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, + jbyteArray storage) { + static bool gInited; + + if (!gInited) { + gInputStream_Clazz = env->FindClass("java/io/InputStream"); + RETURN_NULL_IF_NULL(gInputStream_Clazz); + gInputStream_Clazz = (jclass)env->NewGlobalRef(gInputStream_Clazz); + + gInputStream_resetMethodID = env->GetMethodID(gInputStream_Clazz, + "reset", "()V"); + gInputStream_availableMethodID = env->GetMethodID(gInputStream_Clazz, + "available", "()I"); + gInputStream_readMethodID = env->GetMethodID(gInputStream_Clazz, + "read", "([BII)I"); + gInputStream_skipMethodID = env->GetMethodID(gInputStream_Clazz, + "skip", "(J)J"); + + RETURN_NULL_IF_NULL(gInputStream_resetMethodID); + RETURN_NULL_IF_NULL(gInputStream_availableMethodID); + RETURN_NULL_IF_NULL(gInputStream_availableMethodID); + RETURN_NULL_IF_NULL(gInputStream_skipMethodID); + + gInited = true; + } + + return new JavaInputStreamAdaptor(env, stream, storage); +} + +/////////////////////////////////////////////////////////////////////////////// + +static jclass gOutputStream_Clazz; +static jmethodID gOutputStream_writeMethodID; +static jmethodID gOutputStream_flushMethodID; + +class SkJavaOutputStream : public SkWStream { +public: + SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage) + : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) { + fCapacity = env->GetArrayLength(storage); + } + + virtual bool write(const void* buffer, size_t size) { + JNIEnv* env = fEnv; + jbyteArray storage = fJavaByteArray; + + while (size > 0) { + size_t requested = size; + if (requested > fCapacity) { + requested = fCapacity; + } + + jbyte* array = env->GetByteArrayElements(storage, NULL); + memcpy(array, buffer, requested); + env->ReleaseByteArrayElements(storage, array, 0); + + fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID, + storage, 0, requested); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + printf("------- write threw an exception\n"); + return false; + } + + buffer = (void*)((char*)buffer + requested); + size -= requested; + } + return true; + } + + virtual void flush() { + fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID); + } + +private: + JNIEnv* fEnv; + jobject fJavaOutputStream; // the caller owns this object + jbyteArray fJavaByteArray; // the caller owns this object + size_t fCapacity; +}; + +SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, + jbyteArray storage) { + static bool gInited; + + if (!gInited) { + gOutputStream_Clazz = env->FindClass("java/io/OutputStream"); + RETURN_NULL_IF_NULL(gOutputStream_Clazz); + gOutputStream_Clazz = (jclass)env->NewGlobalRef(gOutputStream_Clazz); + + gOutputStream_writeMethodID = env->GetMethodID(gOutputStream_Clazz, + "write", "([BII)V"); + RETURN_NULL_IF_NULL(gOutputStream_writeMethodID); + gOutputStream_flushMethodID = env->GetMethodID(gOutputStream_Clazz, + "flush", "()V"); + RETURN_NULL_IF_NULL(gOutputStream_flushMethodID); + + gInited = true; + } + + return new SkJavaOutputStream(env, stream, storage); +} + diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h new file mode 100644 index 0000000..cf21dde --- /dev/null +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h @@ -0,0 +1,13 @@ +#ifndef CreateJavaOutputStream_DEFINED +#define CreateJavaOutputStream_DEFINED + +//#include <android_runtime/AndroidRuntime.h> +#include "jni.h" +#include "SkStream.h" + +SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, + jbyteArray storage); +SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, + jbyteArray storage); + +#endif diff --git a/core/jni/android/graphics/DrawFilter.cpp b/core/jni/android/graphics/DrawFilter.cpp new file mode 100644 index 0000000..496e712 --- /dev/null +++ b/core/jni/android/graphics/DrawFilter.cpp @@ -0,0 +1,76 @@ +/* libs/android_runtime/android/graphics/ColorFilter.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// This file was generated from the C++ include file: SkColorFilter.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkDrawFilter.h" +#include "SkPaintFlagsDrawFilter.h" +#include "SkPaint.h" + +namespace android { + +class SkDrawFilterGlue { +public: + + static void finalizer(JNIEnv* env, jobject clazz, SkDrawFilter* obj) { + obj->safeUnref(); + } + + static SkDrawFilter* CreatePaintFlagsDF(JNIEnv* env, jobject clazz, + int clearFlags, int setFlags) { + // trim off any out-of-range bits + clearFlags &= SkPaint::kAllFlags; + setFlags &= SkPaint::kAllFlags; + + if (clearFlags | setFlags) { + return new SkPaintFlagsDrawFilter(clearFlags, setFlags); + } else { + return NULL; + } + } +}; + +static JNINativeMethod drawfilter_methods[] = { + {"nativeDestructor", "(I)V", (void*) SkDrawFilterGlue::finalizer} +}; + +static JNINativeMethod paintflags_methods[] = { + {"nativeConstructor","(II)I", (void*) SkDrawFilterGlue::CreatePaintFlagsDF} +}; + +#define REG(env, name, array) \ + result = android::AndroidRuntime::registerNativeMethods(env, name, array, SK_ARRAY_COUNT(array)); \ + if (result < 0) return result + + +int register_android_graphics_DrawFilter(JNIEnv* env) { + int result; + + REG(env, "android/graphics/DrawFilter", drawfilter_methods); + REG(env, "android/graphics/PaintFlagsDrawFilter", paintflags_methods); + + return 0; +} + +} diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp new file mode 100644 index 0000000..f8ccf72 --- /dev/null +++ b/core/jni/android/graphics/Graphics.cpp @@ -0,0 +1,578 @@ +#include "jni.h" +#include "GraphicsJNI.h" +#include "NIOBuffer.h" +#include "SkPicture.h" +#include "SkRegion.h" +#include <android_runtime/AndroidRuntime.h> + +//#define TRACK_LOCK_COUNT + +void doThrow(JNIEnv* env, const char* exc, const char* msg) { + // don't throw a new exception if we already have one pending + if (env->ExceptionCheck() == JNI_FALSE) { + jclass npeClazz; + + npeClazz = env->FindClass(exc); + LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc); + + env->ThrowNew(npeClazz, msg); + } +} + +void doThrowNPE(JNIEnv* env) { + doThrow(env, "java/lang/NullPointerException"); +} + +void doThrowAIOOBE(JNIEnv* env) { + doThrow(env, "java/lang/ArrayIndexOutOfBoundsException"); +} + +void doThrowRE(JNIEnv* env, const char* msg) { + doThrow(env, "java/lang/RuntimeException", msg); +} + +void doThrowIAE(JNIEnv* env, const char* msg) { + doThrow(env, "java/lang/IllegalArgumentException", msg); +} + +void doThrowISE(JNIEnv* env, const char* msg) { + doThrow(env, "java/lang/IllegalStateException", msg); +} + +void doThrowOOME(JNIEnv* env, const char* msg) { + doThrow(env, "java/lang/OutOfMemoryError", msg); +} + +bool GraphicsJNI::hasException(JNIEnv *env) { + if (env->ExceptionCheck() != 0) { + LOGE("*** Uncaught exception returned from Java call!\n"); + env->ExceptionDescribe(); + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +AutoJavaFloatArray::AutoJavaFloatArray(JNIEnv* env, jfloatArray array, + int minLength) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + SkASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + sk_throw(); + } + fPtr = env->GetFloatArrayElements(array, NULL); + } +} + +AutoJavaFloatArray::~AutoJavaFloatArray() { + if (fPtr) { + fEnv->ReleaseFloatArrayElements(fArray, fPtr, 0); + } +} + +AutoJavaIntArray::AutoJavaIntArray(JNIEnv* env, jintArray array, + int minLength) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + SkASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + sk_throw(); + } + fPtr = env->GetIntArrayElements(array, NULL); + } +} + +AutoJavaIntArray::~AutoJavaIntArray() { + if (fPtr) { + fEnv->ReleaseIntArrayElements(fArray, fPtr, 0); + } +} + +AutoJavaShortArray::AutoJavaShortArray(JNIEnv* env, jshortArray array, + int minLength) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + SkASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + sk_throw(); + } + fPtr = env->GetShortArrayElements(array, NULL); + } +} + +AutoJavaShortArray::~AutoJavaShortArray() { + if (fPtr) { + fEnv->ReleaseShortArrayElements(fArray, fPtr, 0); + } +} + +AutoJavaByteArray::AutoJavaByteArray(JNIEnv* env, jbyteArray array, + int minLength) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + SkASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + sk_throw(); + } + fPtr = env->GetByteArrayElements(array, NULL); + } +} + +AutoJavaByteArray::~AutoJavaByteArray() { + if (fPtr) { + fEnv->ReleaseByteArrayElements(fArray, fPtr, 0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static jclass gRect_class; +static jfieldID gRect_leftFieldID; +static jfieldID gRect_topFieldID; +static jfieldID gRect_rightFieldID; +static jfieldID gRect_bottomFieldID; + +static jclass gRectF_class; +static jfieldID gRectF_leftFieldID; +static jfieldID gRectF_topFieldID; +static jfieldID gRectF_rightFieldID; +static jfieldID gRectF_bottomFieldID; + +static jclass gPoint_class; +static jfieldID gPoint_xFieldID; +static jfieldID gPoint_yFieldID; + +static jclass gPointF_class; +static jfieldID gPointF_xFieldID; +static jfieldID gPointF_yFieldID; + +static jclass gBitmap_class; +static jfieldID gBitmap_nativeInstanceID; +static jmethodID gBitmap_constructorMethodID; +static jmethodID gBitmap_allocBufferMethodID; + +static jclass gBitmapConfig_class; +static jfieldID gBitmapConfig_nativeInstanceID; + +static jclass gCanvas_class; +static jfieldID gCanvas_nativeInstanceID; + +static jclass gPaint_class; +static jfieldID gPaint_nativeInstanceID; + +static jclass gPicture_class; +static jfieldID gPicture_nativeInstanceID; + +static jclass gRegion_class; +static jfieldID gRegion_nativeInstanceID; +static jmethodID gRegion_constructorMethodID; + +static jobject gVMRuntime_singleton; +static jmethodID gVMRuntime_trackExternalAllocationMethodID; +static jmethodID gVMRuntime_trackExternalFreeMethodID; + +/////////////////////////////////////////////////////////////////////////////// + +void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B) +{ + SkASSERT(env->IsInstanceOf(obj, gRect_class)); + + *L = env->GetIntField(obj, gRect_leftFieldID); + *T = env->GetIntField(obj, gRect_topFieldID); + *R = env->GetIntField(obj, gRect_rightFieldID); + *B = env->GetIntField(obj, gRect_bottomFieldID); +} + +void GraphicsJNI::set_jrect(JNIEnv* env, jobject obj, int L, int T, int R, int B) +{ + SkASSERT(env->IsInstanceOf(obj, gRect_class)); + + env->SetIntField(obj, gRect_leftFieldID, L); + env->SetIntField(obj, gRect_topFieldID, T); + env->SetIntField(obj, gRect_rightFieldID, R); + env->SetIntField(obj, gRect_bottomFieldID, B); +} + +SkIRect* GraphicsJNI::jrect_to_irect(JNIEnv* env, jobject obj, SkIRect* ir) +{ + SkASSERT(env->IsInstanceOf(obj, gRect_class)); + + ir->set(env->GetIntField(obj, gRect_leftFieldID), + env->GetIntField(obj, gRect_topFieldID), + env->GetIntField(obj, gRect_rightFieldID), + env->GetIntField(obj, gRect_bottomFieldID)); + return ir; +} + +void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj) +{ + SkASSERT(env->IsInstanceOf(obj, gRect_class)); + + env->SetIntField(obj, gRect_leftFieldID, ir.fLeft); + env->SetIntField(obj, gRect_topFieldID, ir.fTop); + env->SetIntField(obj, gRect_rightFieldID, ir.fRight); + env->SetIntField(obj, gRect_bottomFieldID, ir.fBottom); +} + +SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r) +{ + SkASSERT(env->IsInstanceOf(obj, gRectF_class)); + + r->set(SkFloatToScalar(env->GetFloatField(obj, gRectF_leftFieldID)), + SkFloatToScalar(env->GetFloatField(obj, gRectF_topFieldID)), + SkFloatToScalar(env->GetFloatField(obj, gRectF_rightFieldID)), + SkFloatToScalar(env->GetFloatField(obj, gRectF_bottomFieldID))); + return r; +} + +SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r) +{ + SkASSERT(env->IsInstanceOf(obj, gRect_class)); + + r->set(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)), + SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)), + SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)), + SkIntToScalar(env->GetIntField(obj, gRect_bottomFieldID))); + return r; +} + +void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj) +{ + SkASSERT(env->IsInstanceOf(obj, gRectF_class)); + + env->SetFloatField(obj, gRectF_leftFieldID, SkScalarToFloat(r.fLeft)); + env->SetFloatField(obj, gRectF_topFieldID, SkScalarToFloat(r.fTop)); + env->SetFloatField(obj, gRectF_rightFieldID, SkScalarToFloat(r.fRight)); + env->SetFloatField(obj, gRectF_bottomFieldID, SkScalarToFloat(r.fBottom)); +} + +SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point) +{ + SkASSERT(env->IsInstanceOf(obj, gPoint_class)); + + point->set(env->GetIntField(obj, gPoint_xFieldID), + env->GetIntField(obj, gPoint_yFieldID)); + return point; +} + +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); +} + +SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point) +{ + SkASSERT(env->IsInstanceOf(obj, gPointF_class)); + + point->set(SkFloatToScalar(env->GetIntField(obj, gPointF_xFieldID)), + SkFloatToScalar(env->GetIntField(obj, gPointF_yFieldID))); + return point; +} + +void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj) +{ + SkASSERT(env->IsInstanceOf(obj, gPointF_class)); + + env->SetFloatField(obj, gPointF_xFieldID, SkScalarToFloat(r.fX)); + env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY)); +} + +SkBitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) { + SkASSERT(env); + SkASSERT(bitmap); + SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); + SkBitmap* b = (SkBitmap*)env->GetIntField(bitmap, gBitmap_nativeInstanceID); + SkASSERT(b); + return b; +} + +SkBitmap::Config GraphicsJNI::getNativeBitmapConfig(JNIEnv* env, + jobject jconfig) { + SkASSERT(env); + if (NULL == jconfig) { + return SkBitmap::kNo_Config; + } + SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class)); + int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID); + if (c < 0 || c >= SkBitmap::kConfigCount) { + c = SkBitmap::kNo_Config; + } + return static_cast<SkBitmap::Config>(c); +} + +SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) { + SkASSERT(env); + SkASSERT(canvas); + SkASSERT(env->IsInstanceOf(canvas, gCanvas_class)); + SkCanvas* c = (SkCanvas*)env->GetIntField(canvas, gCanvas_nativeInstanceID); + SkASSERT(c); + return c; +} + +SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) { + SkASSERT(env); + SkASSERT(paint); + SkASSERT(env->IsInstanceOf(paint, gPaint_class)); + SkPaint* p = (SkPaint*)env->GetIntField(paint, gPaint_nativeInstanceID); + SkASSERT(p); + return p; +} + +SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) +{ + SkASSERT(env); + SkASSERT(picture); + SkASSERT(env->IsInstanceOf(picture, gPicture_class)); + SkPicture* p = (SkPicture*)env->GetIntField(picture, gPicture_nativeInstanceID); + SkASSERT(p); + return p; +} + +SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) +{ + SkASSERT(env); + SkASSERT(region); + SkASSERT(env->IsInstanceOf(region, gRegion_class)); + SkRegion* r = (SkRegion*)env->GetIntField(region, gRegion_nativeInstanceID); + SkASSERT(r); + return r; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, + jbyteArray ninepatch) +{ + SkASSERT(bitmap != NULL); + SkASSERT(NULL != bitmap->pixelRef()); + + jobject obj = env->AllocObject(gBitmap_class); + if (obj) { + env->CallVoidMethod(obj, gBitmap_constructorMethodID, + (jint)bitmap, isMutable, ninepatch); + if (hasException(env)) { + obj = NULL; + } + } + return obj; +} + +jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) +{ + SkASSERT(region != NULL); + jobject obj = env->AllocObject(gRegion_class); + if (obj) { + env->CallVoidMethod(obj, gRegion_constructorMethodID, (jint)region, 0); + if (hasException(env)) { + obj = NULL; + } + } + return obj; +} + +#include "SkPixelRef.h" + +static JNIEnv* vm2env(JavaVM* vm) +{ + JNIEnv* env = NULL; + if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK || NULL == env) + { + SkDebugf("------- [%p] vm->GetEnv() failed\n", vm); + sk_throw(); + } + return env; +} + +#ifdef TRACK_LOCK_COUNT + static int gLockCount; +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkMallocPixelRef.h" + +/* Extend SkMallocPixelRef to inform the VM when we free up the storage +*/ +class AndroidPixelRef : public SkMallocPixelRef { +public: + /** Allocate the specified buffer for pixels. The memory is freed when the + last owner of this pixelref is gone. Our caller has already informed + the VM of our allocation. + */ + AndroidPixelRef(JNIEnv* env, void* storage, size_t size, + SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) { + SkASSERT(storage); + SkASSERT(env); + + if (env->GetJavaVM(&fVM) != JNI_OK) { + SkDebugf("------ [%p] env->GetJavaVM failed\n", env); + sk_throw(); + } + } + + virtual ~AndroidPixelRef() { + JNIEnv* env = vm2env(fVM); +// SkDebugf("-------------- inform VM we're releasing %d bytes\n", this->getSize()); + jlong jsize = this->getSize(); // the VM wants longs for the size + env->CallVoidMethod(gVMRuntime_singleton, + gVMRuntime_trackExternalFreeMethodID, + jsize); + if (GraphicsJNI::hasException(env)) { + env->ExceptionClear(); + } + } + +private: + JavaVM* fVM; +}; + +bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, + SkColorTable* ctable) { + Sk64 size64 = bitmap->getSize64(); + if (size64.isNeg() || !size64.is32()) { + doThrow(env, "java/lang/IllegalArgumentException", + "bitmap size exceeds 32bits"); + return false; + } + + size_t size = size64.get32(); + // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size); + jlong jsize = size; // the VM wants longs for the size + bool r = env->CallBooleanMethod(gVMRuntime_singleton, + gVMRuntime_trackExternalAllocationMethodID, + jsize); + if (GraphicsJNI::hasException(env)) { + return false; + } + if (!r) { + LOGE("VM won't let us allocate %zd bytes\n", size); + doThrowOOME(env, "bitmap size exceeds VM budget"); + return false; + } + + // call the version of malloc that returns null on failure + void* addr = sk_malloc_flags(size, 0); + if (NULL == addr) { + // SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size); + // we didn't actually allocate it, so inform the VM + env->CallVoidMethod(gVMRuntime_singleton, + gVMRuntime_trackExternalFreeMethodID, + jsize); + if (!GraphicsJNI::hasException(env)) { + doThrowOOME(env, "bitmap size too large for malloc"); + } + return false; + } + + bitmap->setPixelRef(new AndroidPixelRef(env, addr, size, ctable))->unref(); + // since we're already allocated, we lockPixels right away + // HeapAllocator behaves this way too + bitmap->lockPixels(); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) : fEnv(env) +{ +} + +bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { + return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable); +} + +//////////////////////////////////////////////////////////////////////////////// + +static jclass make_globalref(JNIEnv* env, const char classname[]) +{ + jclass c = env->FindClass(classname); + SkASSERT(c); + return (jclass)env->NewGlobalRef(c); +} + +static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, + const char fieldname[], const char type[]) +{ + jfieldID id = env->GetFieldID(clazz, fieldname, type); + SkASSERT(id); + return id; +} + +int register_android_graphics_Graphics(JNIEnv* env) +{ + jmethodID m; + jclass c; + + gRect_class = make_globalref(env, "android/graphics/Rect"); + gRect_leftFieldID = getFieldIDCheck(env, gRect_class, "left", "I"); + gRect_topFieldID = getFieldIDCheck(env, gRect_class, "top", "I"); + gRect_rightFieldID = getFieldIDCheck(env, gRect_class, "right", "I"); + gRect_bottomFieldID = getFieldIDCheck(env, gRect_class, "bottom", "I"); + + gRectF_class = make_globalref(env, "android/graphics/RectF"); + gRectF_leftFieldID = getFieldIDCheck(env, gRectF_class, "left", "F"); + gRectF_topFieldID = getFieldIDCheck(env, gRectF_class, "top", "F"); + gRectF_rightFieldID = getFieldIDCheck(env, gRectF_class, "right", "F"); + gRectF_bottomFieldID = getFieldIDCheck(env, gRectF_class, "bottom", "F"); + + gPoint_class = make_globalref(env, "android/graphics/Point"); + gPoint_xFieldID = getFieldIDCheck(env, gPoint_class, "x", "I"); + gPoint_yFieldID = getFieldIDCheck(env, gPoint_class, "y", "I"); + + gPointF_class = make_globalref(env, "android/graphics/PointF"); + gPointF_xFieldID = getFieldIDCheck(env, gPointF_class, "x", "F"); + gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F"); + + gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); + gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); + gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", + "(IZ[B)V"); + + gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config"); + gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class, + "nativeInt", "I"); + + gCanvas_class = make_globalref(env, "android/graphics/Canvas"); + gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I"); + + gPaint_class = make_globalref(env, "android/graphics/Paint"); + gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I"); + + gPicture_class = make_globalref(env, "android/graphics/Picture"); + gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "I"); + + gRegion_class = make_globalref(env, "android/graphics/Region"); + gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "I"); + gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>", + "(II)V"); + + // Get the VMRuntime class. + c = env->FindClass("dalvik/system/VMRuntime"); + SkASSERT(c); + // Look up VMRuntime.getRuntime(). + m = env->GetStaticMethodID(c, "getRuntime", "()Ldalvik/system/VMRuntime;"); + SkASSERT(m); + // Call VMRuntime.getRuntime() and hold onto its result. + gVMRuntime_singleton = env->CallStaticObjectMethod(c, m); + SkASSERT(gVMRuntime_singleton); + gVMRuntime_singleton = (jobject)env->NewGlobalRef(gVMRuntime_singleton); + // Look up the VMRuntime methods we'll be using. + gVMRuntime_trackExternalAllocationMethodID = + env->GetMethodID(c, "trackExternalAllocation", "(J)Z"); + gVMRuntime_trackExternalFreeMethodID = + env->GetMethodID(c, "trackExternalFree", "(J)V"); + + NIOBuffer::RegisterJNI(env); + + return 0; +} + diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h new file mode 100644 index 0000000..e67b20b --- /dev/null +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -0,0 +1,156 @@ +#ifndef GraphicsJNI_DEFINED +#define GraphicsJNI_DEFINED + +#include "SkPoint.h" +#include "SkRect.h" +#include "SkBitmap.h" +#include <jni.h> + +class SkCanvas; +class SkPaint; +class SkPicture; + +class GraphicsJNI { +public: + // returns true if an exception is set (and dumps it out to the Log) + static bool hasException(JNIEnv*); + + static void get_jrect(JNIEnv*, jobject jrect, int* L, int* T, int* R, int* B); + static void set_jrect(JNIEnv*, jobject jrect, int L, int T, int R, int B); + + static SkIRect* jrect_to_irect(JNIEnv*, jobject jrect, SkIRect*); + static void irect_to_jrect(const SkIRect&, JNIEnv*, jobject jrect); + + static SkRect* jrectf_to_rect(JNIEnv*, jobject jrectf, SkRect*); + static SkRect* jrect_to_rect(JNIEnv*, jobject jrect, SkRect*); + static void rect_to_jrectf(const SkRect&, JNIEnv*, jobject jrectf); + + static void set_jpoint(JNIEnv*, jobject jrect, int x, int y); + + static SkIPoint* jpoint_to_ipoint(JNIEnv*, jobject jpoint, SkIPoint* point); + static void ipoint_to_jpoint(const SkIPoint& point, JNIEnv*, jobject jpoint); + + static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point); + static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf); + + static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas); + static SkPaint* getNativePaint(JNIEnv*, jobject paint); + static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap); + static SkPicture* getNativePicture(JNIEnv*, jobject picture); + static SkRegion* getNativeRegion(JNIEnv*, jobject region); + + /** Return the corresponding native config from the java Config enum, + or kNo_Config if the java object is null. + */ + static SkBitmap::Config getNativeBitmapConfig(JNIEnv*, jobject jconfig); + + /** Create a java Bitmap object given the native bitmap (required) and optional + storage array (may be null). If storage is specified, then it must already be + locked, and its native address set as the bitmap's pixels. If storage is null, + then the bitmap must be an owner of its natively allocated pixels (via allocPixels). + */ + static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, + jbyteArray ninePatch); + + static jobject createRegion(JNIEnv* env, SkRegion* region); + + /** Set a pixelref for the bitmap (needs setConfig to already be called) + Returns true on success. If it returns false, then it failed, and the + appropriate exception will have been raised. + */ + static bool setJavaPixelRef(JNIEnv*, SkBitmap*, SkColorTable* ctable); + + /** Copy the colors in colors[] to the bitmap, convert to the correct + format along the way. + */ + static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset, + int srcStride, int x, int y, int width, int height, + const SkBitmap& dstBitmap); +}; + +class JavaPixelAllocator : public SkBitmap::Allocator { +public: + JavaPixelAllocator(JNIEnv* env); + // overrides + virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable); + +private: + JNIEnv* fEnv; +}; + +class AutoJavaFloatArray { +public: + AutoJavaFloatArray(JNIEnv* env, jfloatArray array, int minLength = 0); + ~AutoJavaFloatArray(); + + float* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jfloatArray fArray; + float* fPtr; + int fLen; +}; + +class AutoJavaIntArray { +public: + AutoJavaIntArray(JNIEnv* env, jintArray array, int minLength = 0); + ~AutoJavaIntArray(); + + jint* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jintArray fArray; + jint* fPtr; + int fLen; +}; + +class AutoJavaShortArray { +public: + AutoJavaShortArray(JNIEnv* env, jshortArray array, int minLength = 0); + ~AutoJavaShortArray(); + + jshort* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jshortArray fArray; + jshort* fPtr; + int fLen; +}; + +class AutoJavaByteArray { +public: + AutoJavaByteArray(JNIEnv* env, jbyteArray array, int minLength = 0); + ~AutoJavaByteArray(); + + jbyte* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jbyteArray fArray; + jbyte* fPtr; + int fLen; +}; + +void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL); +void doThrowNPE(JNIEnv* env); +void doThrowAIOOBE(JNIEnv* env); // Array Index Out Of Bounds Exception +void doThrowIAE(JNIEnv* env, const char* msg = NULL); // Illegal Argument +void doThrowRE(JNIEnv* env, const char* msg = NULL); // Runtime +void doThrowISE(JNIEnv* env, const char* msg = NULL); // Illegal State +void doThrowOOME(JNIEnv* env, const char* msg = NULL); // Out of memory + +#define NPE_CHECK_RETURN_ZERO(env, object) \ + do { if (NULL == (object)) { doThrowNPE(env); return 0; } } while (0) + +#define NPE_CHECK_RETURN_VOID(env, object) \ + do { if (NULL == (object)) { doThrowNPE(env); return; } } while (0) + +#endif + diff --git a/core/jni/android/graphics/Interpolator.cpp b/core/jni/android/graphics/Interpolator.cpp new file mode 100644 index 0000000..beec351 --- /dev/null +++ b/core/jni/android/graphics/Interpolator.cpp @@ -0,0 +1,97 @@ +#include "jni.h" +#include <android_runtime/AndroidRuntime.h> + +#include "GraphicsJNI.h" +#include "SkInterpolator.h" +#include "SkTemplates.h" + +static SkInterpolator* Interpolator_constructor(JNIEnv* env, jobject clazz, int valueCount, int frameCount) +{ + return new SkInterpolator(valueCount, frameCount); +} + +static void Interpolator_destructor(JNIEnv* env, jobject clazz, SkInterpolator* interp) +{ + delete interp; +} + +static void Interpolator_reset(JNIEnv* env, jobject clazz, SkInterpolator* interp, int valueCount, int frameCount) +{ + interp->reset(valueCount, frameCount); +} + +static void Interpolator_setKeyFrame(JNIEnv* env, jobject clazz, SkInterpolator* interp, int index, int msec, jfloatArray valueArray, jfloatArray blendArray) +{ + SkScalar blendStorage[4]; + SkScalar* blend = NULL; + + AutoJavaFloatArray autoValues(env, valueArray); + float* values = autoValues.ptr(); + int i, n = autoValues.length(); + + SkAutoSTMalloc<16, SkScalar> storage(n); + SkScalar* scalars = storage.get(); + + for (i = 0; i < n; i++) + scalars[i] = SkFloatToScalar(values[i]); + + if (blendArray != NULL) { + AutoJavaFloatArray autoBlend(env, blendArray, 4); + values = autoBlend.ptr(); + for (i = 0; i < 4; i++) + blendStorage[i] = SkFloatToScalar(values[i]); + blend = blendStorage; + } + + interp->setKeyFrame(index, msec, scalars, blend); +} + +static void Interpolator_setRepeatMirror(JNIEnv* env, jobject clazz, SkInterpolator* interp, float repeatCount, jboolean mirror) +{ + if (repeatCount > 32000) + repeatCount = 32000; + + interp->setRepeatCount(SkFloatToScalar(repeatCount)); + interp->setMirror(mirror != 0); +} + +static int Interpolator_timeToValues(JNIEnv* env, jobject clazz, SkInterpolator* interp, int msec, jfloatArray valueArray) +{ + SkInterpolatorBase::Result result; + + float* values = valueArray ? env->GetFloatArrayElements(valueArray, NULL) : NULL; + result = interp->timeToValues(msec, (SkScalar*)values); + + if (valueArray) { + int n = env->GetArrayLength(valueArray); + for (int i = 0; i < n; i++) { + values[i] = SkScalarToFloat(*(SkScalar*)&values[i]); + } + env->ReleaseFloatArrayElements(valueArray, values, 0); + } + + return result; +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gInterpolatorMethods[] = { + { "nativeConstructor", "(II)I", (void*)Interpolator_constructor }, + { "nativeDestructor", "(I)V", (void*)Interpolator_destructor }, + { "nativeReset", "(III)V", (void*)Interpolator_reset }, + { "nativeSetKeyFrame", "(III[F[F)V", (void*)Interpolator_setKeyFrame }, + { "nativeSetRepeatMirror", "(IFZ)V", (void*)Interpolator_setRepeatMirror }, + { "nativeTimeToValues", "(II[F)I", (void*)Interpolator_timeToValues } +}; + +int register_android_graphics_Interpolator(JNIEnv* env); +int register_android_graphics_Interpolator(JNIEnv* env) +{ + return android::AndroidRuntime::registerNativeMethods(env, + "android/graphics/Interpolator", + gInterpolatorMethods, + SK_ARRAY_COUNT(gInterpolatorMethods)); +} diff --git a/core/jni/android/graphics/LayerRasterizer.cpp b/core/jni/android/graphics/LayerRasterizer.cpp new file mode 100644 index 0000000..7c45889 --- /dev/null +++ b/core/jni/android/graphics/LayerRasterizer.cpp @@ -0,0 +1,34 @@ +#include "SkLayerRasterizer.h" +#include <jni.h> + +class SkLayerRasterizerGlue { +public: + static SkRasterizer* create(JNIEnv* env, jobject) { + return new SkLayerRasterizer(); + } + + static void addLayer(JNIEnv* env, jobject, SkLayerRasterizer* layer, const SkPaint* paint, float dx, float dy) { + SkASSERT(layer); + SkASSERT(paint); + layer->addLayer(*paint, SkFloatToScalar(dx), SkFloatToScalar(dy)); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +#include <android_runtime/AndroidRuntime.h> + +static JNINativeMethod gLayerRasterizerMethods[] = { + { "nativeConstructor", "()I", (void*)SkLayerRasterizerGlue::create }, + { "nativeAddLayer", "(IIFF)V", (void*)SkLayerRasterizerGlue::addLayer } +}; + +int register_android_graphics_LayerRasterizer(JNIEnv* env); +int register_android_graphics_LayerRasterizer(JNIEnv* env) +{ + return android::AndroidRuntime::registerNativeMethods(env, + "android/graphics/LayerRasterizer", + gLayerRasterizerMethods, + SK_ARRAY_COUNT(gLayerRasterizerMethods)); +} + diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp new file mode 100644 index 0000000..e6048cd --- /dev/null +++ b/core/jni/android/graphics/MaskFilter.cpp @@ -0,0 +1,61 @@ +#include "GraphicsJNI.h" +#include "SkMaskFilter.h" +#include "SkBlurMaskFilter.h" + +#include <jni.h> + +class SkMaskFilterGlue { +public: + static void destructor(JNIEnv* env, jobject, SkMaskFilter* filter) { + SkASSERT(filter); + filter->unref(); + } + + static SkMaskFilter* createBlur(JNIEnv* env, jobject, float radius, int blurStyle) { + return SkBlurMaskFilter::Create(SkFloatToScalar(radius), (SkBlurMaskFilter::BlurStyle)blurStyle); + } + + static SkMaskFilter* createEmboss(JNIEnv* env, jobject, jfloatArray dirArray, float ambient, float specular, float radius) { + SkScalar direction[3]; + + AutoJavaFloatArray autoDir(env, dirArray, 3); + float* values = autoDir.ptr(); + for (int i = 0; i < 3; i++) { + direction[i] = SkFloatToScalar(values[i]); + } + + return SkBlurMaskFilter::CreateEmboss(direction, SkFloatToScalar(ambient), + SkFloatToScalar(specular), SkFloatToScalar(radius)); + } +}; + +static JNINativeMethod gMaskFilterMethods[] = { + { "nativeDestructor", "(I)V", (void*)SkMaskFilterGlue::destructor } +}; + +static JNINativeMethod gBlurMaskFilterMethods[] = { + { "nativeConstructor", "(FI)I", (void*)SkMaskFilterGlue::createBlur } +}; + +static JNINativeMethod gEmbossMaskFilterMethods[] = { + { "nativeConstructor", "([FFFF)I", (void*)SkMaskFilterGlue::createEmboss } +}; + +#include <android_runtime/AndroidRuntime.h> + +#define REG(env, name, array) \ + result = android::AndroidRuntime::registerNativeMethods(env, name, array, SK_ARRAY_COUNT(array)); \ + if (result < 0) return result + +int register_android_graphics_MaskFilter(JNIEnv* env); +int register_android_graphics_MaskFilter(JNIEnv* env) +{ + int result; + + REG(env, "android/graphics/MaskFilter", gMaskFilterMethods); + REG(env, "android/graphics/BlurMaskFilter", gBlurMaskFilterMethods); + REG(env, "android/graphics/EmbossMaskFilter", gEmbossMaskFilterMethods); + + return 0; +} + diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp new file mode 100644 index 0000000..b782766 --- /dev/null +++ b/core/jni/android/graphics/Matrix.cpp @@ -0,0 +1,412 @@ +/* libs/android_runtime/android/graphics/Matrix.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// This file was generated from the C++ include file: SkMatrix.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkMatrix.h" +#include "SkTemplates.h" + +namespace android { + +class SkMatrixGlue { +public: + + static void finalizer(JNIEnv* env, jobject clazz, SkMatrix* obj) { + delete obj; + } + + static SkMatrix* create(JNIEnv* env, jobject clazz, const SkMatrix* src) { + SkMatrix* obj = new SkMatrix(); + if (src) + *obj = *src; + else + obj->reset(); + return obj; + } + + static jboolean isIdentity(JNIEnv* env, jobject clazz, SkMatrix* obj) { + return obj->isIdentity(); + } + + static jboolean rectStaysRect(JNIEnv* env, jobject clazz, SkMatrix* obj) { + return obj->rectStaysRect(); + } + + static void reset(JNIEnv* env, jobject clazz, SkMatrix* obj) { + obj->reset(); + } + + static void set(JNIEnv* env, jobject clazz, SkMatrix* obj, SkMatrix* other) { + *obj = *other; + } + + static void setTranslate(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat dx, jfloat dy) { + SkScalar dx_ = SkFloatToScalar(dx); + SkScalar dy_ = SkFloatToScalar(dy); + obj->setTranslate(dx_, dy_); + } + + static void setScale__FFFF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat sx, jfloat sy, jfloat px, jfloat py) { + SkScalar sx_ = SkFloatToScalar(sx); + SkScalar sy_ = SkFloatToScalar(sy); + SkScalar px_ = SkFloatToScalar(px); + SkScalar py_ = SkFloatToScalar(py); + obj->setScale(sx_, sy_, px_, py_); + } + + static void setScale__FF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat sx, jfloat sy) { + SkScalar sx_ = SkFloatToScalar(sx); + SkScalar sy_ = SkFloatToScalar(sy); + obj->setScale(sx_, sy_); + } + + static void setRotate__FFF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat degrees, jfloat px, jfloat py) { + SkScalar degrees_ = SkFloatToScalar(degrees); + SkScalar px_ = SkFloatToScalar(px); + SkScalar py_ = SkFloatToScalar(py); + obj->setRotate(degrees_, px_, py_); + } + + static void setRotate__F(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat degrees) { + SkScalar degrees_ = SkFloatToScalar(degrees); + obj->setRotate(degrees_); + } + + static void setSinCos__FFFF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat sinValue, jfloat cosValue, jfloat px, jfloat py) { + SkScalar sinValue_ = SkFloatToScalar(sinValue); + SkScalar cosValue_ = SkFloatToScalar(cosValue); + SkScalar px_ = SkFloatToScalar(px); + SkScalar py_ = SkFloatToScalar(py); + obj->setSinCos(sinValue_, cosValue_, px_, py_); + } + + static void setSinCos__FF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat sinValue, jfloat cosValue) { + SkScalar sinValue_ = SkFloatToScalar(sinValue); + SkScalar cosValue_ = SkFloatToScalar(cosValue); + obj->setSinCos(sinValue_, cosValue_); + } + + static void setSkew__FFFF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat kx, jfloat ky, jfloat px, jfloat py) { + SkScalar kx_ = SkFloatToScalar(kx); + SkScalar ky_ = SkFloatToScalar(ky); + SkScalar px_ = SkFloatToScalar(px); + SkScalar py_ = SkFloatToScalar(py); + obj->setSkew(kx_, ky_, px_, py_); + } + + static void setSkew__FF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat kx, jfloat ky) { + SkScalar kx_ = SkFloatToScalar(kx); + SkScalar ky_ = SkFloatToScalar(ky); + obj->setSkew(kx_, ky_); + } + + static jboolean setConcat(JNIEnv* env, jobject clazz, SkMatrix* obj, SkMatrix* a, SkMatrix* b) { + return obj->setConcat(*a, *b); + } + + static jboolean preTranslate(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat dx, jfloat dy) { + SkScalar dx_ = SkFloatToScalar(dx); + SkScalar dy_ = SkFloatToScalar(dy); + return obj->preTranslate(dx_, dy_); + } + + static jboolean preScale__FFFF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat sx, jfloat sy, jfloat px, jfloat py) { + SkScalar sx_ = SkFloatToScalar(sx); + SkScalar sy_ = SkFloatToScalar(sy); + SkScalar px_ = SkFloatToScalar(px); + SkScalar py_ = SkFloatToScalar(py); + return obj->preScale(sx_, sy_, px_, py_); + } + + static jboolean preScale__FF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat sx, jfloat sy) { + SkScalar sx_ = SkFloatToScalar(sx); + SkScalar sy_ = SkFloatToScalar(sy); + return obj->preScale(sx_, sy_); + } + + static jboolean preRotate__FFF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat degrees, jfloat px, jfloat py) { + SkScalar degrees_ = SkFloatToScalar(degrees); + SkScalar px_ = SkFloatToScalar(px); + SkScalar py_ = SkFloatToScalar(py); + return obj->preRotate(degrees_, px_, py_); + } + + static jboolean preRotate__F(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat degrees) { + SkScalar degrees_ = SkFloatToScalar(degrees); + return obj->preRotate(degrees_); + } + + static jboolean preSkew__FFFF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat kx, jfloat ky, jfloat px, jfloat py) { + SkScalar kx_ = SkFloatToScalar(kx); + SkScalar ky_ = SkFloatToScalar(ky); + SkScalar px_ = SkFloatToScalar(px); + SkScalar py_ = SkFloatToScalar(py); + return obj->preSkew(kx_, ky_, px_, py_); + } + + static jboolean preSkew__FF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat kx, jfloat ky) { + SkScalar kx_ = SkFloatToScalar(kx); + SkScalar ky_ = SkFloatToScalar(ky); + return obj->preSkew(kx_, ky_); + } + + static jboolean preConcat(JNIEnv* env, jobject clazz, SkMatrix* obj, SkMatrix* other) { + return obj->preConcat(*other); + } + + static jboolean postTranslate(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat dx, jfloat dy) { + SkScalar dx_ = SkFloatToScalar(dx); + SkScalar dy_ = SkFloatToScalar(dy); + return obj->postTranslate(dx_, dy_); + } + + static jboolean postScale__FFFF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat sx, jfloat sy, jfloat px, jfloat py) { + SkScalar sx_ = SkFloatToScalar(sx); + SkScalar sy_ = SkFloatToScalar(sy); + SkScalar px_ = SkFloatToScalar(px); + SkScalar py_ = SkFloatToScalar(py); + return obj->postScale(sx_, sy_, px_, py_); + } + + static jboolean postScale__FF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat sx, jfloat sy) { + SkScalar sx_ = SkFloatToScalar(sx); + SkScalar sy_ = SkFloatToScalar(sy); + return obj->postScale(sx_, sy_); + } + + static jboolean postRotate__FFF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat degrees, jfloat px, jfloat py) { + SkScalar degrees_ = SkFloatToScalar(degrees); + SkScalar px_ = SkFloatToScalar(px); + SkScalar py_ = SkFloatToScalar(py); + return obj->postRotate(degrees_, px_, py_); + } + + static jboolean postRotate__F(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat degrees) { + SkScalar degrees_ = SkFloatToScalar(degrees); + return obj->postRotate(degrees_); + } + + static jboolean postSkew__FFFF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat kx, jfloat ky, jfloat px, jfloat py) { + SkScalar kx_ = SkFloatToScalar(kx); + SkScalar ky_ = SkFloatToScalar(ky); + SkScalar px_ = SkFloatToScalar(px); + SkScalar py_ = SkFloatToScalar(py); + return obj->postSkew(kx_, ky_, px_, py_); + } + + static jboolean postSkew__FF(JNIEnv* env, jobject clazz, SkMatrix* matrix, jfloat kx, jfloat ky) { + SkScalar kx_ = SkFloatToScalar(kx); + SkScalar ky_ = SkFloatToScalar(ky); + return matrix->postSkew(kx_, ky_); + } + + static jboolean postConcat(JNIEnv* env, jobject clazz, SkMatrix* matrix, SkMatrix* other) { + return matrix->postConcat(*other); + } + + static jboolean setRectToRect(JNIEnv* env, jobject clazz, SkMatrix* matrix, jobject src, jobject dst, SkMatrix::ScaleToFit stf) { + SkRect src_; + GraphicsJNI::jrectf_to_rect(env, src, &src_); + SkRect dst_; + GraphicsJNI::jrectf_to_rect(env, dst, &dst_); + return matrix->setRectToRect(src_, dst_, stf); + } + + static jboolean setPolyToPoly(JNIEnv* env, jobject clazz, SkMatrix* matrix, + jfloatArray jsrc, int srcIndex, + jfloatArray jdst, int dstIndex, int ptCount) { + SkASSERT(srcIndex >= 0); + SkASSERT(dstIndex >= 0); + SkASSERT((unsigned)ptCount <= 4); + + AutoJavaFloatArray autoSrc(env, jsrc, srcIndex + (ptCount << 1)); + AutoJavaFloatArray autoDst(env, jdst, dstIndex + (ptCount << 1)); + float* src = autoSrc.ptr() + srcIndex; + float* dst = autoDst.ptr() + dstIndex; + +#ifdef SK_SCALAR_IS_FIXED + SkPoint srcPt[4], dstPt[4]; + for (int i = 0; i < ptCount; i++) { + int x = i << 1; + int y = x + 1; + srcPt[i].set(SkFloatToScalar(src[x]), SkFloatToScalar(src[y])); + dstPt[i].set(SkFloatToScalar(dst[x]), SkFloatToScalar(dst[y])); + } + return matrix->setPolyToPoly(srcPt, dstPt, ptCount); +#else + return matrix->setPolyToPoly((const SkPoint*)src, (const SkPoint*)dst, + ptCount); +#endif + } + + static jboolean invert(JNIEnv* env, jobject clazz, SkMatrix* matrix, SkMatrix* inverse) { + return matrix->invert(inverse); + } + + static void mapPoints(JNIEnv* env, jobject clazz, SkMatrix* matrix, + jfloatArray dst, int dstIndex, + jfloatArray src, int srcIndex, + int ptCount, bool isPts) { + SkASSERT(ptCount >= 0); + AutoJavaFloatArray autoSrc(env, src, srcIndex + (ptCount << 1)); + AutoJavaFloatArray autoDst(env, dst, dstIndex + (ptCount << 1)); + float* srcArray = autoSrc.ptr() + srcIndex; + float* dstArray = autoDst.ptr() + dstIndex; + +#ifdef SK_SCALAR_IS_FIXED + // we allocate twice the count, 1 set for src, 1 for dst + SkAutoSTMalloc<32, SkPoint> storage(ptCount * 2); + SkPoint* pts = storage.get(); + SkPoint* srcPt = pts; + SkPoint* dstPt = pts + ptCount; + + int i; + for (i = 0; i < ptCount; i++) { + srcPt[i].set(SkFloatToScalar(srcArray[i << 1]), + SkFloatToScalar(srcArray[(i << 1) + 1])); + } + + if (isPts) + matrix->mapPoints(dstPt, srcPt, ptCount); + else + matrix->mapVectors(dstPt, srcPt, ptCount); + + for (i = 0; i < ptCount; i++) { + dstArray[i << 1] = SkScalarToFloat(dstPt[i].fX); + dstArray[(i << 1) + 1] = SkScalarToFloat(dstPt[i].fY); + } +#else + if (isPts) + matrix->mapPoints((SkPoint*)dstArray, (const SkPoint*)srcArray, + ptCount); + else + matrix->mapVectors((SkVector*)dstArray, (const SkVector*)srcArray, + ptCount); +#endif + } + + static jboolean mapRect__RectFRectF(JNIEnv* env, jobject clazz, SkMatrix* matrix, jobjectArray dst, jobject src) { + SkRect dst_, src_; + GraphicsJNI::jrectf_to_rect(env, src, &src_); + jboolean rectStaysRect = matrix->mapRect(&dst_, src_); + GraphicsJNI::rect_to_jrectf(dst_, env, dst); + return rectStaysRect; + } + + static jfloat mapRadius(JNIEnv* env, jobject clazz, SkMatrix* matrix, jfloat radius) { + return SkScalarToFloat(matrix->mapRadius(SkFloatToScalar(radius))); + } + + static void getValues(JNIEnv* env, jobject clazz, SkMatrix* matrix, jfloatArray values) { + AutoJavaFloatArray autoValues(env, values, 9); + float* dst = autoValues.ptr(); + +#ifdef SK_SCALAR_IS_FIXED + for (int i = 0; i < 6; i++) { + dst[i] = SkFixedToFloat(matrix->get(i)); + } + for (int j = 6; j < 9; j++) { + dst[j] = SkFractToFloat(matrix->get(j)); + } +#else + for (int i = 0; i < 9; i++) { + dst[i] = matrix->get(i); + } +#endif + } + + static void setValues(JNIEnv* env, jobject clazz, SkMatrix* matrix, jfloatArray values) { + AutoJavaFloatArray autoValues(env, values, 9); + const float* src = autoValues.ptr(); + +#ifdef SK_SCALAR_IS_FIXED + for (int i = 0; i < 6; i++) { + matrix->set(i, SkFloatToFixed(src[i])); + } + for (int j = 6; j < 9; j++) { + matrix->set(j, SkFloatToFract(src[j])); + } +#else + for (int i = 0; i < 9; i++) { + matrix->set(i, src[i]); + } +#endif + } + + static jboolean equals(JNIEnv* env, jobject clazz, const SkMatrix* a, const SkMatrix* b) { + return *a == *b; + } + }; + +static JNINativeMethod methods[] = { + {"finalizer", "(I)V", (void*) SkMatrixGlue::finalizer}, + {"native_create","(I)I", (void*) SkMatrixGlue::create}, + {"native_isIdentity","(I)Z", (void*) SkMatrixGlue::isIdentity}, + {"native_rectStaysRect","(I)Z", (void*) SkMatrixGlue::rectStaysRect}, + {"native_reset","(I)V", (void*) SkMatrixGlue::reset}, + {"native_set","(II)V", (void*) SkMatrixGlue::set}, + {"native_setTranslate","(IFF)V", (void*) SkMatrixGlue::setTranslate}, + {"native_setScale","(IFFFF)V", (void*) SkMatrixGlue::setScale__FFFF}, + {"native_setScale","(IFF)V", (void*) SkMatrixGlue::setScale__FF}, + {"native_setRotate","(IFFF)V", (void*) SkMatrixGlue::setRotate__FFF}, + {"native_setRotate","(IF)V", (void*) SkMatrixGlue::setRotate__F}, + {"native_setSinCos","(IFFFF)V", (void*) SkMatrixGlue::setSinCos__FFFF}, + {"native_setSinCos","(IFF)V", (void*) SkMatrixGlue::setSinCos__FF}, + {"native_setSkew","(IFFFF)V", (void*) SkMatrixGlue::setSkew__FFFF}, + {"native_setSkew","(IFF)V", (void*) SkMatrixGlue::setSkew__FF}, + {"native_setConcat","(III)Z", (void*) SkMatrixGlue::setConcat}, + {"native_preTranslate","(IFF)Z", (void*) SkMatrixGlue::preTranslate}, + {"native_preScale","(IFFFF)Z", (void*) SkMatrixGlue::preScale__FFFF}, + {"native_preScale","(IFF)Z", (void*) SkMatrixGlue::preScale__FF}, + {"native_preRotate","(IFFF)Z", (void*) SkMatrixGlue::preRotate__FFF}, + {"native_preRotate","(IF)Z", (void*) SkMatrixGlue::preRotate__F}, + {"native_preSkew","(IFFFF)Z", (void*) SkMatrixGlue::preSkew__FFFF}, + {"native_preSkew","(IFF)Z", (void*) SkMatrixGlue::preSkew__FF}, + {"native_preConcat","(II)Z", (void*) SkMatrixGlue::preConcat}, + {"native_postTranslate","(IFF)Z", (void*) SkMatrixGlue::postTranslate}, + {"native_postScale","(IFFFF)Z", (void*) SkMatrixGlue::postScale__FFFF}, + {"native_postScale","(IFF)Z", (void*) SkMatrixGlue::postScale__FF}, + {"native_postRotate","(IFFF)Z", (void*) SkMatrixGlue::postRotate__FFF}, + {"native_postRotate","(IF)Z", (void*) SkMatrixGlue::postRotate__F}, + {"native_postSkew","(IFFFF)Z", (void*) SkMatrixGlue::postSkew__FFFF}, + {"native_postSkew","(IFF)Z", (void*) SkMatrixGlue::postSkew__FF}, + {"native_postConcat","(II)Z", (void*) SkMatrixGlue::postConcat}, + {"native_setRectToRect","(ILandroid/graphics/RectF;Landroid/graphics/RectF;I)Z", (void*) SkMatrixGlue::setRectToRect}, + {"native_setPolyToPoly","(I[FI[FII)Z", (void*) SkMatrixGlue::setPolyToPoly}, + {"native_invert","(II)Z", (void*) SkMatrixGlue::invert}, + {"native_mapPoints","(I[FI[FIIZ)V", (void*) SkMatrixGlue::mapPoints}, + {"native_mapRect","(ILandroid/graphics/RectF;Landroid/graphics/RectF;)Z", (void*) SkMatrixGlue::mapRect__RectFRectF}, + {"native_mapRadius","(IF)F", (void*) SkMatrixGlue::mapRadius}, + {"native_getValues","(I[F)V", (void*) SkMatrixGlue::getValues}, + {"native_setValues","(I[F)V", (void*) SkMatrixGlue::setValues}, + {"native_equals", "(II)Z", (void*) SkMatrixGlue::equals} +}; + +int register_android_graphics_Matrix(JNIEnv* env) { + int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/Matrix", methods, + sizeof(methods) / sizeof(methods[0])); + return result; +} + +} diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp new file mode 100644 index 0000000..5c48077 --- /dev/null +++ b/core/jni/android/graphics/Movie.cpp @@ -0,0 +1,155 @@ +#include "SkMovie.h" +#include "SkStream.h" +#include "GraphicsJNI.h" +#include "SkTemplates.h" +#include "SkUtils.h" +#include "CreateJavaOutputStreamAdaptor.h" + +#include <utils/Asset.h> +#include <utils/ResourceTypes.h> +#include <netinet/in.h> + +#if 0 + #define TRACE_BITMAP(code) code +#else + #define TRACE_BITMAP(code) +#endif + +static jclass gMovie_class; +static jmethodID gMovie_constructorMethodID; +static jfieldID gMovie_nativeInstanceID; + +jobject create_jmovie(JNIEnv* env, SkMovie* moov) { + if (NULL == moov) { + return NULL; + } + jobject obj = env->AllocObject(gMovie_class); + if (obj) { + env->CallVoidMethod(obj, gMovie_constructorMethodID, (jint)moov); + } + return obj; +} + +static SkMovie* J2Movie(JNIEnv* env, jobject movie) { + SkASSERT(env); + SkASSERT(movie); + SkASSERT(env->IsInstanceOf(movie, gMovie_class)); + SkMovie* m = (SkMovie*)env->GetIntField(movie, gMovie_nativeInstanceID); + SkASSERT(m); + return m; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int movie_width(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return J2Movie(env, movie)->width(); +} + +static int movie_height(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return J2Movie(env, movie)->height(); +} + +static jboolean movie_isOpaque(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return J2Movie(env, movie)->isOpaque(); +} + +static int movie_duration(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return J2Movie(env, movie)->duration(); +} + +static jboolean movie_setTime(JNIEnv* env, jobject movie, int ms) { + NPE_CHECK_RETURN_ZERO(env, movie); + return J2Movie(env, movie)->setTime(ms); +} + +static void movie_draw(JNIEnv* env, jobject movie, jobject canvas, + jfloat fx, jfloat fy, jobject jpaint) { + NPE_CHECK_RETURN_VOID(env, movie); + NPE_CHECK_RETURN_VOID(env, canvas); + // its OK for paint to be null + + SkMovie* m = J2Movie(env, movie); + SkCanvas* c = GraphicsJNI::getNativeCanvas(env, canvas); + SkScalar sx = SkFloatToScalar(fx); + SkScalar sy = SkFloatToScalar(fy); + const SkBitmap& b = m->bitmap(); + const SkPaint* p = jpaint ? GraphicsJNI::getNativePaint(env, jpaint) : NULL; + + c->drawBitmap(b, sx, sy, p); +} + +static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) { + + NPE_CHECK_RETURN_ZERO(env, istream); + + // what is the lifetime of the array? Can the skstream hold onto it? + jbyteArray byteArray = env->NewByteArray(16*1024); + SkStream* strm = CreateJavaInputStreamAdaptor(env, istream, byteArray); + if (NULL == strm) { + return 0; + } + + SkMovie* moov = SkMovie::DecodeStream(strm); + strm->unref(); + return create_jmovie(env, moov); +} + +static jobject movie_decodeByteArray(JNIEnv* env, jobject clazz, + jbyteArray byteArray, + int offset, int length) { + + NPE_CHECK_RETURN_ZERO(env, byteArray); + + int totalLength = env->GetArrayLength(byteArray); + if ((offset | length) < 0 || offset + length > totalLength) { + doThrow(env, "java/lang/ArrayIndexException"); + return 0; + } + + AutoJavaByteArray ar(env, byteArray); + SkMovie* moov = SkMovie::DecodeMemory(ar.ptr() + offset, length); + return create_jmovie(env, moov); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +#include <android_runtime/AndroidRuntime.h> + +static JNINativeMethod gMethods[] = { + { "width", "()I", (void*)movie_width }, + { "height", "()I", (void*)movie_height }, + { "isOpaque", "()Z", (void*)movie_isOpaque }, + { "duration", "()I", (void*)movie_duration }, + { "setTime", "(I)Z", (void*)movie_setTime }, + { "draw", "(Landroid/graphics/Canvas;FFLandroid/graphics/Paint;)V", + (void*)movie_draw }, + { "decodeStream", "(Ljava/io/InputStream;)Landroid/graphics/Movie;", + (void*)movie_decodeStream }, + { "decodeByteArray", "([BII)Landroid/graphics/Movie;", + (void*)movie_decodeByteArray }, +}; + +#define kClassPathName "android/graphics/Movie" + +#define RETURN_ERR_IF_NULL(value) do { if (!(value)) { assert(0); return -1; } } while (false) + +int register_android_graphics_Movie(JNIEnv* env); +int register_android_graphics_Movie(JNIEnv* env) +{ + gMovie_class = env->FindClass(kClassPathName); + RETURN_ERR_IF_NULL(gMovie_class); + gMovie_class = (jclass)env->NewGlobalRef(gMovie_class); + + gMovie_constructorMethodID = env->GetMethodID(gMovie_class, "<init>", "(I)V"); + RETURN_ERR_IF_NULL(gMovie_constructorMethodID); + + gMovie_nativeInstanceID = env->GetFieldID(gMovie_class, "mNativeMovie", "I"); + RETURN_ERR_IF_NULL(gMovie_nativeInstanceID); + + return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, + gMethods, SK_ARRAY_COUNT(gMethods)); +} diff --git a/core/jni/android/graphics/NIOBuffer.cpp b/core/jni/android/graphics/NIOBuffer.cpp new file mode 100644 index 0000000..cb937a3 --- /dev/null +++ b/core/jni/android/graphics/NIOBuffer.cpp @@ -0,0 +1,143 @@ +#include "NIOBuffer.h" +#include "GraphicsJNI.h" + +// enable this to dump each time we ref/unref a global java object (buffer) +// +//#define TRACE_GLOBAL_REFS + +//#define TRACE_ARRAY_LOCKS + +static jclass gNIOAccess_classID; +static jmethodID gNIOAccess_getBasePointer; +static jmethodID gNIOAccess_getBaseArray; +static jmethodID gNIOAccess_getBaseArrayOffset; +static jmethodID gNIOAccess_getRemainingBytes; + +void NIOBuffer::RegisterJNI(JNIEnv* env) { + if (0 != gNIOAccess_classID) { + return; // already called + } + + jclass c = env->FindClass("java/nio/NIOAccess"); + gNIOAccess_classID = (jclass)env->NewGlobalRef(c); + + gNIOAccess_getBasePointer = env->GetStaticMethodID(gNIOAccess_classID, + "getBasePointer", "(Ljava/nio/Buffer;)J"); + gNIOAccess_getBaseArray = env->GetStaticMethodID(gNIOAccess_classID, + "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); + gNIOAccess_getBaseArrayOffset = env->GetStaticMethodID(gNIOAccess_classID, + "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); + gNIOAccess_getRemainingBytes = env->GetStaticMethodID(gNIOAccess_classID, + "getRemainingBytes", "(Ljava/nio/Buffer;)I"); +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef TRACE_GLOBAL_REFS + static int gGlobalRefs; +#endif + +#ifdef TRACE_ARRAY_LOCKS + static int gLockCount; +#endif + +NIOBuffer::NIOBuffer(JNIEnv* env, jobject buffer) { + fBuffer = env->NewGlobalRef(buffer); +#ifdef TRACE_GLOBAL_REFS + SkDebugf("------------ newglobalref bbuffer %X %d\n", buffer, gGlobalRefs++); +#endif + fLockedPtr = NULL; + fLockedArray = NULL; +} + +NIOBuffer::~NIOBuffer() { + // free() needs to have already been called + if (NULL != fBuffer) { + SkDebugf("----- leaked fBuffer in NIOBuffer"); + sk_throw(); + } +} + +void NIOBuffer::free(JNIEnv* env) { + + if (NULL != fLockedPtr) { + SkDebugf("======= free: array still locked %x %p\n", fLockedArray, fLockedPtr); + } + + + if (NULL != fBuffer) { +#ifdef TRACE_GLOBAL_REFS + SkDebugf("----------- deleteglobalref buffer %X %d\n", fBuffer, --gGlobalRefs); +#endif + env->DeleteGlobalRef(fBuffer); + fBuffer = NULL; + } +} + +void* NIOBuffer::lock(JNIEnv* env, int* remaining) { + if (NULL != fLockedPtr) { + SkDebugf("======= lock: array still locked %x %p\n", fLockedArray, fLockedPtr); + } + + fLockedPtr = NULL; + fLockedArray = NULL; + + if (NULL != remaining) { + *remaining = env->CallStaticIntMethod(gNIOAccess_classID, + gNIOAccess_getRemainingBytes, + fBuffer); + if (GraphicsJNI::hasException(env)) { + return NULL; + } + } + + jlong pointer = env->CallStaticLongMethod(gNIOAccess_classID, + gNIOAccess_getBasePointer, + fBuffer); + if (GraphicsJNI::hasException(env)) { + return NULL; + } + if (0 != pointer) { + return reinterpret_cast<void*>(pointer); + } + + fLockedArray = (jbyteArray)env->CallStaticObjectMethod(gNIOAccess_classID, + gNIOAccess_getBaseArray, + fBuffer); + if (GraphicsJNI::hasException(env) || NULL == fLockedArray) { + return NULL; + } + jint offset = env->CallStaticIntMethod(gNIOAccess_classID, + gNIOAccess_getBaseArrayOffset, + fBuffer); + fLockedPtr = env->GetByteArrayElements(fLockedArray, NULL); + if (GraphicsJNI::hasException(env)) { + SkDebugf("------------ failed to lockarray %x\n", fLockedArray); + return NULL; + } +#ifdef TRACE_ARRAY_LOCKS + SkDebugf("------------ lockarray %x %p %d\n", + fLockedArray, fLockedPtr, gLockCount++); +#endif + if (NULL == fLockedPtr) { + offset = 0; + } + return (char*)fLockedPtr + offset; +} + +void NIOBuffer::unlock(JNIEnv* env, bool dataChanged) { + if (NULL != fLockedPtr) { +#ifdef TRACE_ARRAY_LOCKS + SkDebugf("------------ unlockarray %x %p %d\n", + fLockedArray, fLockedPtr, --gLockCount); +#endif + env->ReleaseByteArrayElements(fLockedArray, (jbyte*)fLockedPtr, + dataChanged ? 0 : JNI_ABORT); + + fLockedPtr = NULL; + fLockedArray = NULL; + } else { + SkDebugf("============= unlock called with null ptr %x\n", fLockedArray); + } +} + diff --git a/core/jni/android/graphics/NIOBuffer.h b/core/jni/android/graphics/NIOBuffer.h new file mode 100644 index 0000000..36b5554 --- /dev/null +++ b/core/jni/android/graphics/NIOBuffer.h @@ -0,0 +1,27 @@ +#ifndef NIOBuffer_DEFINED +#define NIOBuffer_DEFINED + +#include <jni.h> +#include "SkBitmap.h" + +class NIOBuffer { +public: + NIOBuffer(JNIEnv* env, jobject buffer); + // this checks to ensure that free() was called + ~NIOBuffer(); + + void* lock(JNIEnv* env, int* remaining); + void unlock(JNIEnv* env, bool dataChanged); + // must be called before destructor + void free(JNIEnv* env); + + // call once on boot, to setup JNI globals + static void RegisterJNI(JNIEnv*); + +private: + jobject fBuffer; + void* fLockedPtr; + jbyteArray fLockedArray; +}; + +#endif diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp new file mode 100644 index 0000000..a098d89 --- /dev/null +++ b/core/jni/android/graphics/NinePatch.cpp @@ -0,0 +1,142 @@ +#include <utils/ResourceTypes.h> + +#include "SkRegion.h" +#include "GraphicsJNI.h" + +#include "JNIHelp.h" + +extern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, + const SkBitmap& bitmap, const android::Res_png_9patch& chunk, + const SkPaint* paint, SkRegion** outRegion); + +using namespace android; + +class SkNinePatchGlue { +public: + static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) + { + if (NULL == obj) { + return false; + } + 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; + } + return false; + } + + static void validateNinePatchChunk(JNIEnv* env, jobject, jint, jbyteArray obj) + { + if (env->GetArrayLength(obj) < (int) (sizeof(Res_png_9patch))) { + jniThrowException(env, "java/lang/RuntimeException", + "Array too small for chunk."); + return; + } + + // XXX Also check that dimensions are correct. + } + + 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) + { + 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()); + memcpy(chunk, array, chunkSize); + Res_png_9patch::deserialize(chunk); + NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); + env->ReleaseByteArrayElements(chunkObj, array, 0); + } + } + + static void drawF(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRectF, + const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint) + { + SkASSERT(canvas); + SkASSERT(boundsRectF); + SkASSERT(bitmap); + SkASSERT(chunkObj); + // paint is optional + + SkRect bounds; + GraphicsJNI::jrectf_to_rect(env, boundsRectF, &bounds); + + draw(env, canvas, bounds, bitmap, chunkObj, paint); + } + + static void drawI(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRect, + const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint) + { + SkASSERT(canvas); + SkASSERT(boundsRect); + SkASSERT(bitmap); + SkASSERT(chunkObj); + // paint is optional + + SkRect bounds; + GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); + draw(env, canvas, bounds, bitmap, chunkObj, paint); + } + + static jint getTransparentRegion(JNIEnv* env, jobject, + const SkBitmap* bitmap, jbyteArray chunkObj, + jobject boundsRect) + { + SkASSERT(bitmap); + SkASSERT(chunkObj); + SkASSERT(boundsRect); + + SkRect bounds; + GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); + 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()); + memcpy(chunk, array, chunkSize); + Res_png_9patch::deserialize(chunk); + SkRegion* region = NULL; + NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, ®ion); + env->ReleaseByteArrayElements(chunkObj, array, 0); + return (jint)region; + } + + return 0; + } + +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +#include <android_runtime/AndroidRuntime.h> + +static JNINativeMethod gNinePatchMethods[] = { + { "isNinePatchChunk", "([B)Z", (void*)SkNinePatchGlue::isNinePatchChunk }, + { "validateNinePatchChunk", "(I[B)V", (void*)SkNinePatchGlue::validateNinePatchChunk }, + { "nativeDraw", "(ILandroid/graphics/RectF;I[BI)V", (void*)SkNinePatchGlue::drawF }, + { "nativeDraw", "(ILandroid/graphics/Rect;I[BI)V", (void*)SkNinePatchGlue::drawI }, + { "nativeGetTransparentRegion", "(I[BLandroid/graphics/Rect;)I", + (void*)SkNinePatchGlue::getTransparentRegion } +}; + +int register_android_graphics_NinePatch(JNIEnv* env); +int register_android_graphics_NinePatch(JNIEnv* env) +{ + return android::AndroidRuntime::registerNativeMethods(env, + "android/graphics/NinePatch", + gNinePatchMethods, + SK_ARRAY_COUNT(gNinePatchMethods)); +} diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp new file mode 100644 index 0000000..ba63ae4 --- /dev/null +++ b/core/jni/android/graphics/NinePatchImpl.cpp @@ -0,0 +1,320 @@ +/* +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "NinePatch" + +#include <utils/ResourceTypes.h> + +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkNinePatch.h" +#include "SkPaint.h" +#include "SkUnPreMultiply.h" + +#define USE_TRACEx + +#ifdef USE_TRACE + static bool gTrace; +#endif + +#include "SkColorPriv.h" + +#include <utils/Log.h> + +static bool getColor(const SkBitmap& bitmap, int x, int y, SkColor* c) { + switch (bitmap.getConfig()) { + case SkBitmap::kARGB_8888_Config: + *c = SkUnPreMultiply::PMColorToColor(*bitmap.getAddr32(x, y)); + break; + case SkBitmap::kRGB_565_Config: + *c = SkPixel16ToPixel32(*bitmap.getAddr16(x, y)); + break; + case SkBitmap::kARGB_4444_Config: + *c = SkUnPreMultiply::PMColorToColor( + SkPixel4444ToPixel32(*bitmap.getAddr16(x, y))); + break; + case SkBitmap::kIndex8_Config: { + SkColorTable* ctable = bitmap.getColorTable(); + *c = SkUnPreMultiply::PMColorToColor( + (*ctable)[*bitmap.getAddr8(x, y)]); + break; + } + default: + return false; + } + return true; +} + +static SkColor modAlpha(SkColor c, int alpha) { + int scale = alpha + (alpha >> 7); + int a = SkColorGetA(c) * scale >> 8; + return SkColorSetA(c, a); +} + +static void drawStretchyPatch(SkCanvas* canvas, SkIRect& src, const SkRect& dst, + const SkBitmap& bitmap, const SkPaint& paint, + SkColor initColor, uint32_t colorHint, + bool hasXfer) { + if (colorHint != android::Res_png_9patch::NO_COLOR) { + ((SkPaint*)&paint)->setColor(modAlpha(colorHint, paint.getAlpha())); + canvas->drawRect(dst, paint); + ((SkPaint*)&paint)->setColor(initColor); + } else if (src.width() == 1 && src.height() == 1) { + SkColor c; + if (!getColor(bitmap, src.fLeft, src.fTop, &c)) { + goto SLOW_CASE; + } + if (0 != c || hasXfer) { + SkColor prev = paint.getColor(); + ((SkPaint*)&paint)->setColor(c); + canvas->drawRect(dst, paint); + ((SkPaint*)&paint)->setColor(prev); + } + } else { + SLOW_CASE: + canvas->drawBitmapRect(bitmap, &src, dst, &paint); + } +} + +SkScalar calculateStretch(SkScalar boundsLimit, SkScalar startingPoint, + int srcSpace, int numStrechyPixelsRemaining, + int numFixedPixelsRemaining) { + SkScalar spaceRemaining = boundsLimit - startingPoint; + SkScalar stretchySpaceRemaining = + spaceRemaining - SkIntToScalar(numFixedPixelsRemaining); + return SkScalarMulDiv(srcSpace, stretchySpaceRemaining, + numStrechyPixelsRemaining); +} + +void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, + const SkBitmap& bitmap, const android::Res_png_9patch& chunk, + const SkPaint* paint, SkRegion** outRegion) { + // 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)) { + SkNinePatch::DrawMesh(canvas, bounds, bitmap, + chunk.xDivs, chunk.numXDivs, + chunk.yDivs, chunk.numYDivs, + paint); + return; + } + +#ifdef USE_TRACE + gTrace = true; +#endif + + SkASSERT(canvas || outRegion); + +#if 0 + if (canvas) { + const SkMatrix& m = canvas->getTotalMatrix(); + SkDebugf("ninepatch [%g %g %g] [%g %g %g]\n", + SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]), + SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5])); + } +#endif + +#ifdef USE_TRACE + if (gTrace) { + SkDEBUGF(("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()))); + SkDEBUGF(("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height())); + SkDEBUGF(("======== ninepatch xDivs [%d,%d]\n", chunk.xDivs[0], chunk.xDivs[1])); + SkDEBUGF(("======== ninepatch yDivs [%d,%d]\n", chunk.yDivs[0], chunk.yDivs[1])); + } +#endif + + if (bounds.isEmpty() || + bitmap.width() == 0 || bitmap.height() == 0 || + (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0)) + { +#ifdef USE_TRACE + if (gTrace) SkDEBUGF(("======== abort ninepatch draw\n")); +#endif + return; + } + + // should try a quick-reject test before calling lockPixels + + SkAutoLockPixels alp(bitmap); + // after the lock, it is valid to check getPixels() + if (bitmap.getPixels() == NULL) + return; + + SkPaint defaultPaint; + if (NULL == paint) { + paint = &defaultPaint; + } + + const bool hasXfer = paint->getXfermode() != NULL; + SkRect dst; + SkIRect src; + + const int32_t x0 = chunk.xDivs[0]; + const int32_t y0 = chunk.yDivs[0]; + const SkColor initColor = ((SkPaint*)paint)->getColor(); + const uint8_t numXDivs = chunk.numXDivs; + const uint8_t numYDivs = chunk.numYDivs; + int i; + int j; + int colorIndex = 0; + uint32_t color; + bool xIsStretchable; + const bool initialXIsStretchable = (x0 == 0); + bool yIsStretchable = (y0 == 0); + const int bitmapWidth = bitmap.width(); + const int bitmapHeight = bitmap.height(); + + SkScalar* dstRights = (SkScalar*) alloca((numXDivs + 1) * sizeof(SkScalar)); + bool dstRightsHaveBeenCached = false; + + int numStretchyXPixelsRemaining = 0; + for (i = 0; i < numXDivs; i += 2) { + numStretchyXPixelsRemaining += chunk.xDivs[i + 1] - chunk.xDivs[i]; + } + int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining; + int numStretchyYPixelsRemaining = 0; + for (i = 0; i < numYDivs; i += 2) { + numStretchyYPixelsRemaining += chunk.yDivs[i + 1] - chunk.yDivs[i]; + } + int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining; + +#if 0 + SkDebugf("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n", + bitmap.width(), bitmap.height(), + SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), + SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()), + numXDivs, numYDivs); +#endif + + src.fTop = 0; + dst.fTop = bounds.fTop; + // The first row always starts with the top being at y=0 and the bottom + // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case + // the first row is stretchable along the Y axis, otherwise it is fixed. + // The last row always ends with the bottom being bitmap.height and the top + // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or + // yDivs[numYDivs-1]. In the former case the last row is stretchable along + // the Y axis, otherwise it is fixed. + // + // The first and last columns are similarly treated with respect to the X + // axis. + // + // The above is to help explain some of the special casing that goes on the + // code below. + + // The initial yDiv and whether the first row is considered stretchable or + // not depends on whether yDiv[0] was zero or not. + for (j = yIsStretchable ? 1 : 0; + j <= numYDivs && src.fTop < bitmapHeight; + j++, yIsStretchable = !yIsStretchable) { + src.fLeft = 0; + dst.fLeft = bounds.fLeft; + if (j == numYDivs) { + src.fBottom = bitmapHeight; + dst.fBottom = bounds.fBottom; + } else { + src.fBottom = chunk.yDivs[j]; + const int srcYSize = src.fBottom - src.fTop; + if (yIsStretchable) { + dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop, + srcYSize, + numStretchyYPixelsRemaining, + numFixedYPixelsRemaining); + numStretchyYPixelsRemaining -= srcYSize; + } else { + dst.fBottom = dst.fTop + SkIntToScalar(srcYSize); + numFixedYPixelsRemaining -= srcYSize; + } + } + + xIsStretchable = initialXIsStretchable; + // The initial xDiv and whether the first column is considered + // stretchable or not depends on whether xDiv[0] was zero or not. + for (i = xIsStretchable ? 1 : 0; + i <= numXDivs && src.fLeft < bitmapWidth; + i++, xIsStretchable = !xIsStretchable) { + color = chunk.colors[colorIndex++]; + if (i == numXDivs) { + src.fRight = bitmapWidth; + dst.fRight = bounds.fRight; + } else { + src.fRight = chunk.xDivs[i]; + if (dstRightsHaveBeenCached) { + dst.fRight = dstRights[i]; + } else { + const int srcXSize = src.fRight - src.fLeft; + if (xIsStretchable) { + dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft, + srcXSize, + numStretchyXPixelsRemaining, + numFixedXPixelsRemaining); + numStretchyXPixelsRemaining -= srcXSize; + } else { + dst.fRight = dst.fLeft + SkIntToScalar(srcXSize); + numFixedXPixelsRemaining -= srcXSize; + } + dstRights[i] = dst.fRight; + } + } + // If this horizontal patch is too small to be displayed, leave + // the destination left edge where it is and go on to the next patch + // in the source. + if (src.fLeft >= src.fRight) { + src.fLeft = src.fRight; + continue; + } + // Make sure that we actually have room to draw any bits + if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) { + goto nextDiv; + } + // If this patch is transparent, skip and don't draw. + if (color == android::Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) { + if (outRegion) { + if (*outRegion == NULL) { + *outRegion = new SkRegion(); + } + SkIRect idst; + dst.round(&idst); + //LOGI("Adding trans rect: (%d,%d)-(%d,%d)\n", + // idst.fLeft, idst.fTop, idst.fRight, idst.fBottom); + (*outRegion)->op(idst, SkRegion::kUnion_Op); + } + goto nextDiv; + } + if (canvas) { +#if 0 + SkDebugf("-- src [%d %d %d %d] dst [%g %g %g %g]\n", + src.fLeft, src.fTop, src.width(), src.height(), + SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop), + SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height())); + if (2 == src.width() && SkIntToScalar(5) == dst.width()) { + SkDebugf("--- skip patch\n"); + } +#endif + drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor, + color, hasXfer); + } + +nextDiv: + src.fLeft = src.fRight; + dst.fLeft = dst.fRight; + } + src.fTop = src.fBottom; + dst.fTop = dst.fBottom; + dstRightsHaveBeenCached = true; + } +} diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp new file mode 100644 index 0000000..05830de --- /dev/null +++ b/core/jni/android/graphics/Paint.cpp @@ -0,0 +1,605 @@ +/* libs/android_runtime/android/graphics/Paint.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// This file was generated from the C++ include file: SkPaint.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkBlurDrawLooper.h" +#include "SkColorFilter.h" +#include "SkMaskFilter.h" +#include "SkRasterizer.h" +#include "SkShader.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +namespace android { + +struct JMetricsID { + jfieldID top; + jfieldID ascent; + jfieldID descent; + jfieldID bottom; + jfieldID leading; +}; + +static jclass gFontMetrics_class; +static JMetricsID gFontMetrics_fieldID; + +static jclass gFontMetricsInt_class; +static JMetricsID gFontMetricsInt_fieldID; + +class SkPaintGlue { +public: + + static void finalizer(JNIEnv* env, jobject clazz, SkPaint* obj) { + delete obj; + } + + static SkPaint* init(JNIEnv* env, jobject clazz) { + SkPaint* obj = new SkPaint(); + // utf16 is required for java + obj->setTextEncoding(SkPaint::kUTF16_TextEncoding); + return obj; + } + + static SkPaint* intiWithPaint(JNIEnv* env, jobject clazz, SkPaint* paint) { + SkPaint* obj = new SkPaint(*paint); + return obj; + } + + static void reset(JNIEnv* env, jobject clazz, SkPaint* obj) { + obj->reset(); + } + + static void assign(JNIEnv* env, jobject clazz, SkPaint* dst, const SkPaint* src) { + *dst = *src; + } + + static jint getFlags(JNIEnv* env, jobject paint) { + NPE_CHECK_RETURN_ZERO(env, paint); + return GraphicsJNI::getNativePaint(env, paint)->getFlags(); + } + + static void setFlags(JNIEnv* env, jobject paint, jint flags) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setFlags(flags); + } + + static void setAntiAlias(JNIEnv* env, jobject paint, jboolean aa) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setAntiAlias(aa); + } + + static void setLinearText(JNIEnv* env, jobject paint, jboolean linearText) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setLinearText(linearText); + } + + static void setSubpixelText(JNIEnv* env, jobject paint, jboolean subpixelText) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setSubpixelText(subpixelText); + } + + static void setUnderlineText(JNIEnv* env, jobject paint, jboolean underlineText) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setUnderlineText(underlineText); + } + + static void setStrikeThruText(JNIEnv* env, jobject paint, jboolean strikeThruText) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setStrikeThruText(strikeThruText); + } + + static void setFakeBoldText(JNIEnv* env, jobject paint, jboolean fakeBoldText) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setFakeBoldText(fakeBoldText); + } + + static void setFilterBitmap(JNIEnv* env, jobject paint, jboolean filterBitmap) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setFilterBitmap(filterBitmap); + } + + static void setDither(JNIEnv* env, jobject paint, jboolean dither) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setDither(dither); + } + + static jint getStyle(JNIEnv* env, jobject clazz, SkPaint* obj) { + return obj->getStyle(); + } + + static void setStyle(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Style style) { + obj->setStyle(style); + } + + static jint getColor(JNIEnv* env, jobject paint) { + NPE_CHECK_RETURN_ZERO(env, paint); + return GraphicsJNI::getNativePaint(env, paint)->getColor(); + } + + static jint getAlpha(JNIEnv* env, jobject paint) { + NPE_CHECK_RETURN_ZERO(env, paint); + return GraphicsJNI::getNativePaint(env, paint)->getAlpha(); + } + + static void setColor(JNIEnv* env, jobject paint, jint color) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setColor(color); + } + + static void setAlpha(JNIEnv* env, jobject paint, jint a) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setAlpha(a); + } + + static jfloat getStrokeWidth(JNIEnv* env, jobject paint) { + NPE_CHECK_RETURN_ZERO(env, paint); + return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getStrokeWidth()); + } + + static void setStrokeWidth(JNIEnv* env, jobject paint, jfloat width) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setStrokeWidth(SkFloatToScalar(width)); + } + + static jfloat getStrokeMiter(JNIEnv* env, jobject paint) { + NPE_CHECK_RETURN_ZERO(env, paint); + return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getStrokeMiter()); + } + + static void setStrokeMiter(JNIEnv* env, jobject paint, jfloat miter) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setStrokeMiter(SkFloatToScalar(miter)); + } + + static jint getStrokeCap(JNIEnv* env, jobject clazz, SkPaint* obj) { + return obj->getStrokeCap(); + } + + static void setStrokeCap(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Cap cap) { + obj->setStrokeCap(cap); + } + + static jint getStrokeJoin(JNIEnv* env, jobject clazz, SkPaint* obj) { + return obj->getStrokeJoin(); + } + + static void setStrokeJoin(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Join join) { + obj->setStrokeJoin(join); + } + + static jboolean getFillPath(JNIEnv* env, jobject clazz, SkPaint* obj, SkPath* src, SkPath* dst) { + return obj->getFillPath(*src, dst); + } + + static SkShader* setShader(JNIEnv* env, jobject clazz, SkPaint* obj, SkShader* shader) { + return obj->setShader(shader); + } + + static SkColorFilter* setColorFilter(JNIEnv* env, jobject clazz, SkPaint* obj, SkColorFilter* filter) { + return obj->setColorFilter(filter); + } + + static SkXfermode* setXfermode(JNIEnv* env, jobject clazz, SkPaint* obj, SkXfermode* xfermode) { + return obj->setXfermode(xfermode); + } + + static SkPathEffect* setPathEffect(JNIEnv* env, jobject clazz, SkPaint* obj, SkPathEffect* effect) { + return obj->setPathEffect(effect); + } + + static SkMaskFilter* setMaskFilter(JNIEnv* env, jobject clazz, SkPaint* obj, SkMaskFilter* maskfilter) { + return obj->setMaskFilter(maskfilter); + } + + static SkTypeface* setTypeface(JNIEnv* env, jobject clazz, SkPaint* obj, SkTypeface* typeface) { + return obj->setTypeface(typeface); + } + + static SkRasterizer* setRasterizer(JNIEnv* env, jobject clazz, SkPaint* obj, SkRasterizer* rasterizer) { + return obj->setRasterizer(rasterizer); + } + + static jint getTextAlign(JNIEnv* env, jobject clazz, SkPaint* obj) { + return obj->getTextAlign(); + } + + static void setTextAlign(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Align align) { + obj->setTextAlign(align); + } + + static jfloat getTextSize(JNIEnv* env, jobject paint) { + NPE_CHECK_RETURN_ZERO(env, paint); + return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getTextSize()); + } + + static void setTextSize(JNIEnv* env, jobject paint, jfloat textSize) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setTextSize(SkFloatToScalar(textSize)); + } + + static jfloat getTextScaleX(JNIEnv* env, jobject paint) { + NPE_CHECK_RETURN_ZERO(env, paint); + return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getTextScaleX()); + } + + static void setTextScaleX(JNIEnv* env, jobject paint, jfloat scaleX) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setTextScaleX(SkFloatToScalar(scaleX)); + } + + static jfloat getTextSkewX(JNIEnv* env, jobject paint) { + NPE_CHECK_RETURN_ZERO(env, paint); + return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getTextSkewX()); + } + + static void setTextSkewX(JNIEnv* env, jobject paint, jfloat skewX) { + NPE_CHECK_RETURN_VOID(env, paint); + GraphicsJNI::getNativePaint(env, paint)->setTextSkewX(SkFloatToScalar(skewX)); + } + + static jfloat ascent(JNIEnv* env, jobject paint) { + NPE_CHECK_RETURN_ZERO(env, paint); + SkPaint::FontMetrics metrics; + (void)GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics); + return SkScalarToFloat(metrics.fAscent); + } + + static jfloat descent(JNIEnv* env, jobject paint) { + NPE_CHECK_RETURN_ZERO(env, paint); + SkPaint::FontMetrics metrics; + (void)GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics); + return SkScalarToFloat(metrics.fDescent); + } + + static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) { + NPE_CHECK_RETURN_ZERO(env, paint); + SkPaint::FontMetrics metrics; + SkScalar spacing = GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics); + + if (metricsObj) { + SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading)); + } + return SkScalarToFloat(spacing); + } + + static jint getFontMetricsInt(JNIEnv* env, jobject paint, jobject metricsObj) { + NPE_CHECK_RETURN_ZERO(env, paint); + SkPaint::FontMetrics metrics; + + GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics); + int ascent = SkScalarRound(metrics.fAscent); + int descent = SkScalarRound(metrics.fDescent); + int leading = SkScalarRound(metrics.fLeading); + + if (metricsObj) { + SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class)); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloor(metrics.fTop)); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeil(metrics.fBottom)); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading); + } + return descent - ascent + leading; + } + + static jfloat measureText_CII(JNIEnv* env, jobject jpaint, jcharArray text, int index, int count) { + 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"); + return 0; + } + + jfloat width = SkScalarToFloat(paint->measureText(textArray + index, count << 1)); + env->ReleaseCharArrayElements(text, textArray, 0); + return width; + } + + static jfloat measureText_StringII(JNIEnv* env, jobject jpaint, jstring text, int start, int end) { + NPE_CHECK_RETURN_ZERO(env, jpaint); + NPE_CHECK_RETURN_ZERO(env, text); + + SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); + const jchar* textArray = env->GetStringChars(text, NULL); + size_t textLength = env->GetStringLength(text); + + int count = end - start; + if ((start | count) < 0 || (size_t)count > textLength) { + doThrow(env, "IndexOutOfBoundsException"); + return 0; + } + + jfloat width = SkScalarToFloat(paint->measureText(textArray + start, count << 1)); + env->ReleaseStringChars(text, textArray); + return width; + } + + static jfloat measureText_String(JNIEnv* env, jobject jpaint, jstring text) { + NPE_CHECK_RETURN_ZERO(env, jpaint); + NPE_CHECK_RETURN_ZERO(env, text); + + SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); + const jchar* textArray = env->GetStringChars(text, NULL); + size_t textLength = env->GetStringLength(text); + + jfloat width = SkScalarToFloat(paint->measureText(textArray, textLength << 1)); + env->ReleaseStringChars(text, textArray); + return width; + } + + static int dotextwidths(JNIEnv* env, SkPaint* paint, const jchar text[], int count, jfloatArray widths) { + AutoJavaFloatArray autoWidths(env, widths, count); + jfloat* widthsArray = autoWidths.ptr(); + SkScalar* scalarArray = (SkScalar*)widthsArray; + + count = paint->getTextWidths(text, count << 1, scalarArray); + for (int i = 0; i < count; i++) { + widthsArray[i] = SkScalarToFloat(scalarArray[i]); + } + return count; + } + + 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); + count = dotextwidths(env, paint, textArray + index, count, widths); + env->ReleaseCharArrayElements(text, textArray, 0); + return count; + } + + static int getTextWidths__StringII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, int start, int end, jfloatArray widths) { + const jchar* textArray = env->GetStringChars(text, NULL); + int count = dotextwidths(env, paint, textArray + start, end - start, widths); + env->ReleaseStringChars(text, textArray); + return count; + } + + 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); + paint->getTextPath(textArray + index, count << 1, SkFloatToScalar(x), SkFloatToScalar(y), path); + env->ReleaseCharArrayElements(text, textArray, 0); + } + + static void getTextPath__StringIIFFPath(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, int start, int end, jfloat x, jfloat y, SkPath* path) { + const jchar* textArray = env->GetStringChars(text, NULL); + paint->getTextPath(textArray + start, (end - start) << 1, SkFloatToScalar(x), SkFloatToScalar(y), path); + env->ReleaseStringChars(text, textArray); + } + + static void setShadowLayer(JNIEnv* env, jobject jpaint, jfloat radius, + jfloat dx, jfloat dy, int color) { + NPE_CHECK_RETURN_VOID(env, jpaint); + + SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); + if (radius <= 0) { + paint->setLooper(NULL); + } + else { + paint->setLooper(new SkBlurDrawLooper(SkFloatToScalar(radius), + SkFloatToScalar(dx), + SkFloatToScalar(dy), + (SkColor)color))->unref(); + } + } + + static int breakText(JNIEnv* env, const SkPaint& paint, const jchar text[], + int count, float maxWidth, jfloatArray jmeasured, + SkPaint::TextBufferDirection tbd) { + SkASSERT(paint.getTextEncoding() == SkPaint::kUTF16_TextEncoding); + + SkScalar measured; + size_t bytes = paint.breakText(text, count << 1, + SkFloatToScalar(maxWidth), &measured, tbd); + SkASSERT((bytes & 1) == 0); + + if (jmeasured && env->GetArrayLength(jmeasured) > 0) { + AutoJavaFloatArray autoMeasured(env, jmeasured, 1); + jfloat* array = autoMeasured.ptr(); + array[0] = SkScalarToFloat(measured); + } + return bytes >> 1; + } + + static int breakTextC(JNIEnv* env, jobject jpaint, jcharArray jtext, + int index, int count, float maxWidth, jfloatArray jmeasuredWidth) { + NPE_CHECK_RETURN_ZERO(env, jpaint); + NPE_CHECK_RETURN_ZERO(env, jtext); + + SkPaint::TextBufferDirection tbd; + if (count < 0) { + tbd = SkPaint::kBackward_TextBufferDirection; + count = -count; + } + else { + tbd = SkPaint::kForward_TextBufferDirection; + } + + if ((index < 0) || (index + count > env->GetArrayLength(jtext))) { + doThrow(env, "java/lang/ArrayIndexOutOfBoundsException"); + return 0; + } + + SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); + jchar* text = env->GetCharArrayElements(jtext, NULL); + count = breakText(env, *paint, text + index, count, maxWidth, + jmeasuredWidth, tbd); + env->ReleaseCharArrayElements(jtext, text, 0); + return count; + } + + static int breakTextS(JNIEnv* env, jobject jpaint, jstring jtext, + bool forwards, float maxWidth, jfloatArray jmeasuredWidth) { + NPE_CHECK_RETURN_ZERO(env, jpaint); + NPE_CHECK_RETURN_ZERO(env, jtext); + + SkPaint::TextBufferDirection tbd = forwards ? + SkPaint::kForward_TextBufferDirection : + SkPaint::kBackward_TextBufferDirection; + + SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); + int count = env->GetStringLength(jtext); + const jchar* text = env->GetStringChars(jtext, NULL); + count = breakText(env, *paint, text, count, maxWidth, + jmeasuredWidth, tbd); + env->ReleaseStringChars(jtext, text); + return count; + } + + static void doTextBounds(JNIEnv* env, const jchar* text, int count, + jobject bounds, const SkPaint& paint) + { + SkRect r; + SkIRect ir; + + paint.measureText(text, count << 1, &r); + r.roundOut(&ir); + GraphicsJNI::irect_to_jrect(ir, env, bounds); + } + + static void getStringBounds(JNIEnv* env, jobject, const SkPaint* paint, + jstring text, int start, int end, jobject bounds) + { + const jchar* textArray = env->GetStringChars(text, NULL); + doTextBounds(env, textArray + start, end - start, bounds, *paint); + env->ReleaseStringChars(text, textArray); + } + + static void getCharArrayBounds(JNIEnv* env, jobject, const SkPaint* paint, + jcharArray text, int index, int count, jobject bounds) + { + jchar* textArray = env->GetCharArrayElements(text, NULL); + doTextBounds(env, textArray + index, count, bounds, *paint); + env->ReleaseCharArrayElements(text, textArray, 0); + } + +}; + +static JNINativeMethod methods[] = { + {"finalizer", "(I)V", (void*) SkPaintGlue::finalizer}, + {"native_init","()I", (void*) SkPaintGlue::init}, + {"native_initWithPaint","(I)I", (void*) SkPaintGlue::intiWithPaint}, + {"native_reset","(I)V", (void*) SkPaintGlue::reset}, + {"native_set","(II)V", (void*) SkPaintGlue::assign}, + {"getFlags","()I", (void*) SkPaintGlue::getFlags}, + {"setFlags","(I)V", (void*) SkPaintGlue::setFlags}, + {"setAntiAlias","(Z)V", (void*) SkPaintGlue::setAntiAlias}, + {"setSubpixelText","(Z)V", (void*) SkPaintGlue::setSubpixelText}, + {"setLinearText","(Z)V", (void*) SkPaintGlue::setLinearText}, + {"setUnderlineText","(Z)V", (void*) SkPaintGlue::setUnderlineText}, + {"setStrikeThruText","(Z)V", (void*) SkPaintGlue::setStrikeThruText}, + {"setFakeBoldText","(Z)V", (void*) SkPaintGlue::setFakeBoldText}, + {"setFilterBitmap","(Z)V", (void*) SkPaintGlue::setFilterBitmap}, + {"setDither","(Z)V", (void*) SkPaintGlue::setDither}, + {"native_getStyle","(I)I", (void*) SkPaintGlue::getStyle}, + {"native_setStyle","(II)V", (void*) SkPaintGlue::setStyle}, + {"getColor","()I", (void*) SkPaintGlue::getColor}, + {"setColor","(I)V", (void*) SkPaintGlue::setColor}, + {"getAlpha","()I", (void*) SkPaintGlue::getAlpha}, + {"setAlpha","(I)V", (void*) SkPaintGlue::setAlpha}, + {"getStrokeWidth","()F", (void*) SkPaintGlue::getStrokeWidth}, + {"setStrokeWidth","(F)V", (void*) SkPaintGlue::setStrokeWidth}, + {"getStrokeMiter","()F", (void*) SkPaintGlue::getStrokeMiter}, + {"setStrokeMiter","(F)V", (void*) SkPaintGlue::setStrokeMiter}, + {"native_getStrokeCap","(I)I", (void*) SkPaintGlue::getStrokeCap}, + {"native_setStrokeCap","(II)V", (void*) SkPaintGlue::setStrokeCap}, + {"native_getStrokeJoin","(I)I", (void*) SkPaintGlue::getStrokeJoin}, + {"native_setStrokeJoin","(II)V", (void*) SkPaintGlue::setStrokeJoin}, + {"native_getFillPath","(III)Z", (void*) SkPaintGlue::getFillPath}, + {"native_setShader","(II)I", (void*) SkPaintGlue::setShader}, + {"native_setColorFilter","(II)I", (void*) SkPaintGlue::setColorFilter}, + {"native_setXfermode","(II)I", (void*) SkPaintGlue::setXfermode}, + {"native_setPathEffect","(II)I", (void*) SkPaintGlue::setPathEffect}, + {"native_setMaskFilter","(II)I", (void*) SkPaintGlue::setMaskFilter}, + {"native_setTypeface","(II)I", (void*) SkPaintGlue::setTypeface}, + {"native_setRasterizer","(II)I", (void*) SkPaintGlue::setRasterizer}, + {"native_getTextAlign","(I)I", (void*) SkPaintGlue::getTextAlign}, + {"native_setTextAlign","(II)V", (void*) SkPaintGlue::setTextAlign}, + {"getTextSize","()F", (void*) SkPaintGlue::getTextSize}, + {"setTextSize","(F)V", (void*) SkPaintGlue::setTextSize}, + {"getTextScaleX","()F", (void*) SkPaintGlue::getTextScaleX}, + {"setTextScaleX","(F)V", (void*) SkPaintGlue::setTextScaleX}, + {"getTextSkewX","()F", (void*) SkPaintGlue::getTextSkewX}, + {"setTextSkewX","(F)V", (void*) SkPaintGlue::setTextSkewX}, + {"ascent","()F", (void*) SkPaintGlue::ascent}, + {"descent","()F", (void*) SkPaintGlue::descent}, + {"getFontMetrics", "(Landroid/graphics/Paint$FontMetrics;)F", (void*)SkPaintGlue::getFontMetrics}, + {"getFontMetricsInt", "(Landroid/graphics/Paint$FontMetricsInt;)I", (void*)SkPaintGlue::getFontMetricsInt}, + {"measureText","([CII)F", (void*) SkPaintGlue::measureText_CII}, + {"measureText","(Ljava/lang/String;)F", (void*) SkPaintGlue::measureText_String}, + {"measureText","(Ljava/lang/String;II)F", (void*) SkPaintGlue::measureText_StringII}, + {"breakText","([CIIF[F)I", (void*) SkPaintGlue::breakTextC}, + {"breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS}, + {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F}, + {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F}, + {"native_getTextPath","(I[CIIFFI)V", (void*) SkPaintGlue::getTextPath___CIIFFPath}, + {"native_getTextPath","(ILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__StringIIFFPath}, + {"nativeGetStringBounds", "(ILjava/lang/String;IILandroid/graphics/Rect;)V", + (void*) SkPaintGlue::getStringBounds }, + {"nativeGetCharArrayBounds", "(I[CIILandroid/graphics/Rect;)V", + (void*) SkPaintGlue::getCharArrayBounds }, + {"setShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer} +}; + +static jfieldID req_fieldID(jfieldID id) { + SkASSERT(id); + return id; +} + +int register_android_graphics_Paint(JNIEnv* env) { + gFontMetrics_class = env->FindClass("android/graphics/Paint$FontMetrics"); + SkASSERT(gFontMetrics_class); + gFontMetrics_class = (jclass)env->NewGlobalRef(gFontMetrics_class); + + gFontMetrics_fieldID.top = req_fieldID(env->GetFieldID(gFontMetrics_class, "top", "F")); + gFontMetrics_fieldID.ascent = req_fieldID(env->GetFieldID(gFontMetrics_class, "ascent", "F")); + gFontMetrics_fieldID.descent = req_fieldID(env->GetFieldID(gFontMetrics_class, "descent", "F")); + gFontMetrics_fieldID.bottom = req_fieldID(env->GetFieldID(gFontMetrics_class, "bottom", "F")); + gFontMetrics_fieldID.leading = req_fieldID(env->GetFieldID(gFontMetrics_class, "leading", "F")); + + gFontMetricsInt_class = env->FindClass("android/graphics/Paint$FontMetricsInt"); + SkASSERT(gFontMetricsInt_class); + gFontMetricsInt_class = (jclass)env->NewGlobalRef(gFontMetricsInt_class); + + gFontMetricsInt_fieldID.top = req_fieldID(env->GetFieldID(gFontMetricsInt_class, "top", "I")); + gFontMetricsInt_fieldID.ascent = req_fieldID(env->GetFieldID(gFontMetricsInt_class, "ascent", "I")); + gFontMetricsInt_fieldID.descent = req_fieldID(env->GetFieldID(gFontMetricsInt_class, "descent", "I")); + gFontMetricsInt_fieldID.bottom = req_fieldID(env->GetFieldID(gFontMetricsInt_class, "bottom", "I")); + gFontMetricsInt_fieldID.leading = req_fieldID(env->GetFieldID(gFontMetricsInt_class, "leading", "I")); + + int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/Paint", methods, + sizeof(methods) / sizeof(methods[0])); + return result; +} + +} diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp new file mode 100644 index 0000000..effb1c8 --- /dev/null +++ b/core/jni/android/graphics/Path.cpp @@ -0,0 +1,306 @@ +/* libs/android_runtime/android/graphics/Path.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// This file was generated from the C++ include file: SkPath.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkPath.h" + +namespace android { + +class SkPathGlue { +public: + + static void finalizer(JNIEnv* env, jobject clazz, SkPath* obj) { + delete obj; + } + + static SkPath* init1(JNIEnv* env, jobject clazz) { + return new SkPath(); + } + + static SkPath* init2(JNIEnv* env, jobject clazz, SkPath* val) { + return new SkPath(*val); + } + + static void reset(JNIEnv* env, jobject clazz, SkPath* obj) { + obj->reset(); + } + + static void rewind(JNIEnv* env, jobject clazz, SkPath* obj) { + obj->rewind(); + } + + static void assign(JNIEnv* env, jobject clazz, SkPath* dst, const SkPath* src) { + *dst = *src; + } + + static jint getFillType(JNIEnv* env, jobject clazz, SkPath* obj) { + return obj->getFillType(); + } + + static void setFillType(JNIEnv* env, jobject clazz, SkPath* path, + SkPath::FillType ft) { + path->setFillType(ft); + } + + static jboolean isEmpty(JNIEnv* env, jobject clazz, SkPath* obj) { + return obj->isEmpty(); + } + + static jboolean isRect(JNIEnv* env, jobject clazz, SkPath* obj, jobject rect) { + SkRect rect_; + jboolean result = obj->isRect(&rect_); + GraphicsJNI::rect_to_jrectf(rect_, env, rect); + return result; + } + + static void computeBounds(JNIEnv* env, jobject clazz, SkPath* obj, jobject bounds, SkPath::BoundsType btype) { + SkRect bounds_; + obj->computeBounds(&bounds_, btype); + GraphicsJNI::rect_to_jrectf(bounds_, env, bounds); + } + + static void incReserve(JNIEnv* env, jobject clazz, SkPath* obj, jint extraPtCount) { + obj->incReserve(extraPtCount); + } + + static void moveTo__FF(JNIEnv* env, jobject clazz, SkPath* obj, jfloat x, jfloat y) { + SkScalar x_ = SkFloatToScalar(x); + SkScalar y_ = SkFloatToScalar(y); + obj->moveTo(x_, y_); + } + + static void rMoveTo(JNIEnv* env, jobject clazz, SkPath* obj, jfloat dx, jfloat dy) { + SkScalar dx_ = SkFloatToScalar(dx); + SkScalar dy_ = SkFloatToScalar(dy); + obj->rMoveTo(dx_, dy_); + } + + static void lineTo__FF(JNIEnv* env, jobject clazz, SkPath* obj, jfloat x, jfloat y) { + SkScalar x_ = SkFloatToScalar(x); + SkScalar y_ = SkFloatToScalar(y); + obj->lineTo(x_, y_); + } + + static void rLineTo(JNIEnv* env, jobject clazz, SkPath* obj, jfloat dx, jfloat dy) { + SkScalar dx_ = SkFloatToScalar(dx); + SkScalar dy_ = SkFloatToScalar(dy); + obj->rLineTo(dx_, dy_); + } + + static void quadTo__FFFF(JNIEnv* env, jobject clazz, SkPath* obj, jfloat x1, jfloat y1, jfloat x2, jfloat y2) { + SkScalar x1_ = SkFloatToScalar(x1); + SkScalar y1_ = SkFloatToScalar(y1); + SkScalar x2_ = SkFloatToScalar(x2); + SkScalar y2_ = SkFloatToScalar(y2); + obj->quadTo(x1_, y1_, x2_, y2_); + } + + static void rQuadTo(JNIEnv* env, jobject clazz, SkPath* obj, jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2) { + SkScalar dx1_ = SkFloatToScalar(dx1); + SkScalar dy1_ = SkFloatToScalar(dy1); + SkScalar dx2_ = SkFloatToScalar(dx2); + SkScalar dy2_ = SkFloatToScalar(dy2); + obj->rQuadTo(dx1_, dy1_, dx2_, dy2_); + } + + static void cubicTo__FFFFFF(JNIEnv* env, jobject clazz, SkPath* obj, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) { + SkScalar x1_ = SkFloatToScalar(x1); + SkScalar y1_ = SkFloatToScalar(y1); + SkScalar x2_ = SkFloatToScalar(x2); + SkScalar y2_ = SkFloatToScalar(y2); + SkScalar x3_ = SkFloatToScalar(x3); + SkScalar y3_ = SkFloatToScalar(y3); + obj->cubicTo(x1_, y1_, x2_, y2_, x3_, y3_); + } + + static void rCubicTo(JNIEnv* env, jobject clazz, SkPath* obj, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) { + SkScalar x1_ = SkFloatToScalar(x1); + SkScalar y1_ = SkFloatToScalar(y1); + SkScalar x2_ = SkFloatToScalar(x2); + SkScalar y2_ = SkFloatToScalar(y2); + SkScalar x3_ = SkFloatToScalar(x3); + SkScalar y3_ = SkFloatToScalar(y3); + obj->rCubicTo(x1_, y1_, x2_, y2_, x3_, y3_); + } + + static void arcTo(JNIEnv* env, jobject clazz, SkPath* obj, jobject oval, jfloat startAngle, jfloat sweepAngle, jboolean forceMoveTo) { + SkRect oval_; + GraphicsJNI::jrectf_to_rect(env, oval, &oval_); + SkScalar startAngle_ = SkFloatToScalar(startAngle); + SkScalar sweepAngle_ = SkFloatToScalar(sweepAngle); + obj->arcTo(oval_, startAngle_, sweepAngle_, forceMoveTo); + } + + static void close(JNIEnv* env, jobject clazz, SkPath* obj) { + obj->close(); + } + + static void addRect__RectFI(JNIEnv* env, jobject clazz, SkPath* obj, jobject rect, SkPath::Direction dir) { + SkRect rect_; + GraphicsJNI::jrectf_to_rect(env, rect, &rect_); + obj->addRect(rect_, dir); + } + + static void addRect__FFFFI(JNIEnv* env, jobject clazz, SkPath* obj, jfloat left, jfloat top, jfloat right, jfloat bottom, SkPath::Direction dir) { + SkScalar left_ = SkFloatToScalar(left); + SkScalar top_ = SkFloatToScalar(top); + SkScalar right_ = SkFloatToScalar(right); + SkScalar bottom_ = SkFloatToScalar(bottom); + obj->addRect(left_, top_, right_, bottom_, dir); + } + + static void addOval(JNIEnv* env, jobject clazz, SkPath* obj, jobject oval, SkPath::Direction dir) { + SkRect oval_; + GraphicsJNI::jrectf_to_rect(env, oval, &oval_); + obj->addOval(oval_, dir); + } + + static void addCircle(JNIEnv* env, jobject clazz, SkPath* obj, jfloat x, jfloat y, jfloat radius, SkPath::Direction dir) { + SkScalar x_ = SkFloatToScalar(x); + SkScalar y_ = SkFloatToScalar(y); + SkScalar radius_ = SkFloatToScalar(radius); + obj->addCircle(x_, y_, radius_, dir); + } + + static void addArc(JNIEnv* env, jobject clazz, SkPath* obj, jobject oval, jfloat startAngle, jfloat sweepAngle) { + SkRect oval_; + GraphicsJNI::jrectf_to_rect(env, oval, &oval_); + SkScalar startAngle_ = SkFloatToScalar(startAngle); + SkScalar sweepAngle_ = SkFloatToScalar(sweepAngle); + obj->addArc(oval_, startAngle_, sweepAngle_); + } + + static void addRoundRectXY(JNIEnv* env, jobject clazz, SkPath* obj, jobject rect, + jfloat rx, jfloat ry, SkPath::Direction dir) { + SkRect rect_; + GraphicsJNI::jrectf_to_rect(env, rect, &rect_); + SkScalar rx_ = SkFloatToScalar(rx); + SkScalar ry_ = SkFloatToScalar(ry); + obj->addRoundRect(rect_, rx_, ry_, dir); + } + + static void addRoundRect8(JNIEnv* env, jobject, SkPath* obj, jobject rect, + jfloatArray array, SkPath::Direction dir) { + SkRect rect_; + GraphicsJNI::jrectf_to_rect(env, rect, &rect_); + AutoJavaFloatArray afa(env, array, 8); + const float* src = afa.ptr(); + SkScalar dst[8]; + + for (int i = 0; i < 8; i++) { + dst[i] = SkFloatToScalar(src[i]); + } + obj->addRoundRect(rect_, dst, dir); + } + + static void addPath__PathFF(JNIEnv* env, jobject clazz, SkPath* obj, SkPath* src, jfloat dx, jfloat dy) { + SkScalar dx_ = SkFloatToScalar(dx); + SkScalar dy_ = SkFloatToScalar(dy); + obj->addPath(*src, dx_, dy_); + } + + static void addPath__Path(JNIEnv* env, jobject clazz, SkPath* obj, SkPath* src) { + obj->addPath(*src); + } + + static void addPath__PathMatrix(JNIEnv* env, jobject clazz, SkPath* obj, SkPath* src, SkMatrix* matrix) { + obj->addPath(*src, *matrix); + } + + static void offset__FFPath(JNIEnv* env, jobject clazz, SkPath* obj, jfloat dx, jfloat dy, SkPath* dst) { + SkScalar dx_ = SkFloatToScalar(dx); + SkScalar dy_ = SkFloatToScalar(dy); + obj->offset(dx_, dy_, dst); + } + + static void offset__FF(JNIEnv* env, jobject clazz, SkPath* obj, jfloat dx, jfloat dy) { + SkScalar dx_ = SkFloatToScalar(dx); + SkScalar dy_ = SkFloatToScalar(dy); + obj->offset(dx_, dy_); + } + + static void setLastPoint(JNIEnv* env, jobject clazz, SkPath* obj, jfloat dx, jfloat dy) { + SkScalar dx_ = SkFloatToScalar(dx); + SkScalar dy_ = SkFloatToScalar(dy); + obj->setLastPt(dx_, dy_); + } + + static void transform__MatrixPath(JNIEnv* env, jobject clazz, SkPath* obj, SkMatrix* matrix, SkPath* dst) { + obj->transform(*matrix, dst); + } + + static void transform__Matrix(JNIEnv* env, jobject clazz, SkPath* obj, SkMatrix* matrix) { + obj->transform(*matrix); + } + +}; + +static JNINativeMethod methods[] = { + {"finalizer", "(I)V", (void*) SkPathGlue::finalizer}, + {"init1","()I", (void*) SkPathGlue::init1}, + {"init2","(I)I", (void*) SkPathGlue::init2}, + {"native_reset","(I)V", (void*) SkPathGlue::reset}, + {"native_rewind","(I)V", (void*) SkPathGlue::rewind}, + {"native_set","(II)V", (void*) SkPathGlue::assign}, + {"native_getFillType","(I)I", (void*) SkPathGlue::getFillType}, + {"native_setFillType","(II)V", (void*) SkPathGlue::setFillType}, + {"native_isEmpty","(I)Z", (void*) SkPathGlue::isEmpty}, + {"native_isRect","(ILandroid/graphics/RectF;)Z", (void*) SkPathGlue::isRect}, + {"native_computeBounds","(ILandroid/graphics/RectF;I)V", (void*) SkPathGlue::computeBounds}, + {"native_incReserve","(II)V", (void*) SkPathGlue::incReserve}, + {"native_moveTo","(IFF)V", (void*) SkPathGlue::moveTo__FF}, + {"native_rMoveTo","(IFF)V", (void*) SkPathGlue::rMoveTo}, + {"native_lineTo","(IFF)V", (void*) SkPathGlue::lineTo__FF}, + {"native_rLineTo","(IFF)V", (void*) SkPathGlue::rLineTo}, + {"native_quadTo","(IFFFF)V", (void*) SkPathGlue::quadTo__FFFF}, + {"native_rQuadTo","(IFFFF)V", (void*) SkPathGlue::rQuadTo}, + {"native_cubicTo","(IFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF}, + {"native_rCubicTo","(IFFFFFF)V", (void*) SkPathGlue::rCubicTo}, + {"native_arcTo","(ILandroid/graphics/RectF;FFZ)V", (void*) SkPathGlue::arcTo}, + {"native_close","(I)V", (void*) SkPathGlue::close}, + {"native_addRect","(ILandroid/graphics/RectF;I)V", (void*) SkPathGlue::addRect__RectFI}, + {"native_addRect","(IFFFFI)V", (void*) SkPathGlue::addRect__FFFFI}, + {"native_addOval","(ILandroid/graphics/RectF;I)V", (void*) SkPathGlue::addOval}, + {"native_addCircle","(IFFFI)V", (void*) SkPathGlue::addCircle}, + {"native_addArc","(ILandroid/graphics/RectF;FF)V", (void*) SkPathGlue::addArc}, + {"native_addRoundRect","(ILandroid/graphics/RectF;FFI)V", (void*) SkPathGlue::addRoundRectXY}, + {"native_addRoundRect","(ILandroid/graphics/RectF;[FI)V", (void*) SkPathGlue::addRoundRect8}, + {"native_addPath","(IIFF)V", (void*) SkPathGlue::addPath__PathFF}, + {"native_addPath","(II)V", (void*) SkPathGlue::addPath__Path}, + {"native_addPath","(III)V", (void*) SkPathGlue::addPath__PathMatrix}, + {"native_offset","(IFFI)V", (void*) SkPathGlue::offset__FFPath}, + {"native_offset","(IFF)V", (void*) SkPathGlue::offset__FF}, + {"native_setLastPoint","(IFF)V", (void*) SkPathGlue::setLastPoint}, + {"native_transform","(III)V", (void*) SkPathGlue::transform__MatrixPath}, + {"native_transform","(II)V", (void*) SkPathGlue::transform__Matrix} +}; + +int register_android_graphics_Path(JNIEnv* env) { + int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/Path", methods, + sizeof(methods) / sizeof(methods[0])); + return result; +} + +} diff --git a/core/jni/android/graphics/PathEffect.cpp b/core/jni/android/graphics/PathEffect.cpp new file mode 100644 index 0000000..0ecb004 --- /dev/null +++ b/core/jni/android/graphics/PathEffect.cpp @@ -0,0 +1,113 @@ +#include <jni.h> +#include "GraphicsJNI.h" + +#include "SkPathEffect.h" +#include "SkCornerPathEffect.h" +#include "SkDashPathEffect.h" +#include "SkDiscretePathEffect.h" +#include "Sk1DPathEffect.h" +#include "SkTemplates.h" + +class SkPathEffectGlue { +public: + + static void destructor(JNIEnv* env, jobject, SkPathEffect* effect) { + effect->safeUnref(); + } + + static SkPathEffect* Compose_constructor(JNIEnv* env, jobject, + SkPathEffect* outer, SkPathEffect* inner) { + return new SkComposePathEffect(outer, inner); + } + + static SkPathEffect* Sum_constructor(JNIEnv* env, jobject, + SkPathEffect* first, SkPathEffect* second) { + return new SkSumPathEffect(first, second); + } + + static SkPathEffect* Dash_constructor(JNIEnv* env, jobject, + jfloatArray intervalArray, float phase) { + AutoJavaFloatArray autoInterval(env, intervalArray); + int count = autoInterval.length() & ~1; // even number + float* values = autoInterval.ptr(); + + SkAutoSTMalloc<32, SkScalar> storage(count); + SkScalar* intervals = storage.get(); + for (int i = 0; i < count; i++) { + intervals[i] = SkFloatToScalar(values[i]); + } + return new SkDashPathEffect(intervals, count, SkFloatToScalar(phase)); + } + + static SkPathEffect* OneD_constructor(JNIEnv* env, jobject, + const SkPath* shape, float advance, float phase, int style) { + SkASSERT(shape != NULL); + return new SkPath1DPathEffect(*shape, SkFloatToScalar(advance), + SkFloatToScalar(phase), (SkPath1DPathEffect::Style)style); + } + + static SkPathEffect* Corner_constructor(JNIEnv* env, jobject, float radius){ + return new SkCornerPathEffect(SkFloatToScalar(radius)); + } + + static SkPathEffect* Discrete_constructor(JNIEnv* env, jobject, + float length, float deviation) { + return new SkDiscretePathEffect(SkFloatToScalar(length), + SkFloatToScalar(deviation)); + } + +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +static JNINativeMethod gPathEffectMethods[] = { + { "nativeDestructor", "(I)V", (void*)SkPathEffectGlue::destructor } +}; + +static JNINativeMethod gComposePathEffectMethods[] = { + { "nativeCreate", "(II)I", (void*)SkPathEffectGlue::Compose_constructor } +}; + +static JNINativeMethod gSumPathEffectMethods[] = { + { "nativeCreate", "(II)I", (void*)SkPathEffectGlue::Sum_constructor } +}; + +static JNINativeMethod gDashPathEffectMethods[] = { + { "nativeCreate", "([FF)I", (void*)SkPathEffectGlue::Dash_constructor } +}; + +static JNINativeMethod gPathDashPathEffectMethods[] = { + { "nativeCreate", "(IFFI)I", (void*)SkPathEffectGlue::OneD_constructor } +}; + +static JNINativeMethod gCornerPathEffectMethods[] = { + { "nativeCreate", "(F)I", (void*)SkPathEffectGlue::Corner_constructor } +}; + +static JNINativeMethod gDiscretePathEffectMethods[] = { + { "nativeCreate", "(FF)I", (void*)SkPathEffectGlue::Discrete_constructor } +}; + +#include <android_runtime/AndroidRuntime.h> + +#define REG(env, name, array) \ + result = android::AndroidRuntime::registerNativeMethods(env, name, array, \ + SK_ARRAY_COUNT(array)); \ + if (result < 0) return result + +int register_android_graphics_PathEffect(JNIEnv* env); +int register_android_graphics_PathEffect(JNIEnv* env) +{ + int result; + + REG(env, "android/graphics/PathEffect", gPathEffectMethods); + REG(env, "android/graphics/ComposePathEffect", gComposePathEffectMethods); + REG(env, "android/graphics/SumPathEffect", gSumPathEffectMethods); + REG(env, "android/graphics/DashPathEffect", gDashPathEffectMethods); + REG(env, "android/graphics/PathDashPathEffect", gPathDashPathEffectMethods); + REG(env, "android/graphics/CornerPathEffect", gCornerPathEffectMethods); + REG(env, "android/graphics/DiscretePathEffect", gDiscretePathEffectMethods); + + return 0; +} + diff --git a/core/jni/android/graphics/PathMeasure.cpp b/core/jni/android/graphics/PathMeasure.cpp new file mode 100644 index 0000000..51a3f3a --- /dev/null +++ b/core/jni/android/graphics/PathMeasure.cpp @@ -0,0 +1,138 @@ +/* libs/android_runtime/android/graphics/PathMeasure.cpp +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** 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. +*/ + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkPathMeasure.h" + +/* We declare an explicit pair, so that we don't have to rely on the java + client to be sure not to edit the path while we have an active measure + object associated with it. + + This costs us the copy of the path, for the sake of not allowing a bad + java client to randomly crash (since we can't detect the case where the + native path has been modified). + + The C side does have this risk, but it chooses for speed over safety. If it + later changes this, and is internally safe from changes to the path, then + we can remove this explicit copy from our JNI code. + + Note that we do not have a reference on the java side to the java path. + Were we to not need the native copy here, we would want to add a java + reference, so that the java path would not get GD'd while the measure object + was still alive. +*/ +struct PathMeasurePair { + PathMeasurePair() {} + PathMeasurePair(const SkPath& path, bool forceClosed) + : fPath(path), fMeasure(fPath, forceClosed) {} + + SkPath fPath; // copy of the user's path + SkPathMeasure fMeasure; // this guy points to fPath +}; + +namespace android { + +class SkPathMeasureGlue { +public: + + static PathMeasurePair* create(JNIEnv* env, jobject clazz, const SkPath* path, jboolean forceClosed) { + return path ? new PathMeasurePair(*path, forceClosed) : new PathMeasurePair; + } + + static void setPath(JNIEnv* env, jobject clazz, PathMeasurePair* pair, const SkPath* path, jboolean forceClosed) { + if (NULL == path) { + pair->fPath.reset(); + } else { + pair->fPath = *path; + } + pair->fMeasure.setPath(&pair->fPath, forceClosed); + } + + static jfloat getLength(JNIEnv* env, jobject clazz, PathMeasurePair* pair) { + return SkScalarToFloat(pair->fMeasure.getLength()); + } + + static void convertTwoElemFloatArray(JNIEnv* env, jfloatArray array, const SkScalar src[2]) { + AutoJavaFloatArray autoArray(env, array, 2); + jfloat* ptr = autoArray.ptr(); + ptr[0] = SkScalarToFloat(src[0]); + ptr[1] = SkScalarToFloat(src[1]); + } + + static jboolean getPosTan(JNIEnv* env, jobject clazz, PathMeasurePair* pair, jfloat dist, jfloatArray pos, jfloatArray tan) { + SkScalar tmpPos[2], tmpTan[2]; + SkScalar* posPtr = pos ? tmpPos : NULL; + SkScalar* tanPtr = tan ? tmpTan : NULL; + + if (!pair->fMeasure.getPosTan(SkFloatToScalar(dist), (SkPoint*)posPtr, (SkVector*)tanPtr)) { + return false; + } + + if (pos) { + convertTwoElemFloatArray(env, pos, tmpPos); + } + if (tan) { + convertTwoElemFloatArray(env, tan, tmpTan); + } + return true; + } + + static jboolean getMatrix(JNIEnv* env, jobject clazz, PathMeasurePair* pair, jfloat dist, + SkMatrix* matrix, int flags) { + return pair->fMeasure.getMatrix(SkFloatToScalar(dist), matrix, (SkPathMeasure::MatrixFlags)flags); + } + + static jboolean getSegment(JNIEnv* env, jobject clazz, PathMeasurePair* pair, jfloat startF, + jfloat stopF, SkPath* dst, jboolean startWithMoveTo) { + return pair->fMeasure.getSegment(SkFloatToScalar(startF), SkFloatToScalar(stopF), dst, startWithMoveTo); + } + + static jboolean isClosed(JNIEnv* env, jobject clazz, PathMeasurePair* pair) { + return pair->fMeasure.isClosed(); + } + + static jboolean nextContour(JNIEnv* env, jobject clazz, PathMeasurePair* pair) { + return pair->fMeasure.nextContour(); + } + + static void destroy(JNIEnv* env, jobject clazz, PathMeasurePair* pair) { + delete pair; + } +}; + +static JNINativeMethod methods[] = { + {"native_create", "(IZ)I", (void*) SkPathMeasureGlue::create }, + {"native_setPath", "(IIZ)V", (void*) SkPathMeasureGlue::setPath }, + {"native_getLength", "(I)F", (void*) SkPathMeasureGlue::getLength }, + {"native_getPosTan", "(IF[F[F)Z", (void*) SkPathMeasureGlue::getPosTan }, + {"native_getMatrix", "(IFII)Z", (void*) SkPathMeasureGlue::getMatrix }, + {"native_getSegment", "(IFFIZ)Z", (void*) SkPathMeasureGlue::getSegment }, + {"native_isClosed", "(I)Z", (void*) SkPathMeasureGlue::isClosed }, + {"native_nextContour", "(I)Z", (void*) SkPathMeasureGlue::nextContour }, + {"native_destroy", "(I)V", (void*) SkPathMeasureGlue::destroy } +}; + +int register_android_graphics_PathMeasure(JNIEnv* env) { + int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/PathMeasure", methods, + sizeof(methods) / sizeof(methods[0])); + return result; +} + +} diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp new file mode 100644 index 0000000..5ab6dd3 --- /dev/null +++ b/core/jni/android/graphics/Picture.cpp @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkCanvas.h" +#include "SkPicture.h" +#include "SkTemplates.h" +#include "CreateJavaOutputStreamAdaptor.h" + +namespace android { + +class SkPictureGlue { +public: + static SkPicture* newPicture(JNIEnv* env, jobject, const SkPicture* src) { + if (src) { + return new SkPicture(*src); + } else { + return new SkPicture; + } + } + + static SkPicture* deserialize(JNIEnv* env, jobject, jobject jstream, + jbyteArray jstorage) { + SkPicture* picture = NULL; + SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage); + if (strm) { + picture = new SkPicture(strm); + delete strm; + } + return picture; + } + + static void killPicture(JNIEnv* env, jobject, SkPicture* picture) { + SkASSERT(picture); + delete picture; + } + + static void draw(JNIEnv* env, jobject, SkCanvas* canvas, + SkPicture* picture) { + SkASSERT(canvas); + SkASSERT(picture); + picture->draw(canvas); + } + + static bool serialize(JNIEnv* env, jobject, SkPicture* picture, + jobject jstream, jbyteArray jstorage) { + SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); + + if (NULL != strm) { + picture->serialize(strm); + delete strm; + return true; + } + return false; + } + + static int getWidth(JNIEnv* env, jobject jpic) { + NPE_CHECK_RETURN_ZERO(env, jpic); + return GraphicsJNI::getNativePicture(env, jpic)->width(); + } + + static int getHeight(JNIEnv* env, jobject jpic) { + NPE_CHECK_RETURN_ZERO(env, jpic); + return GraphicsJNI::getNativePicture(env, jpic)->height(); + } + + static SkCanvas* beginRecording(JNIEnv* env, jobject, SkPicture* pict, + int w, int h) { + // beginRecording does not ref its return value, it just returns it. + SkCanvas* canvas = pict->beginRecording(w, h); + // the java side will wrap this guy in a Canvas.java, which will call + // unref in its finalizer, so we have to ref it here, so that both that + // Canvas.java and our picture can both be owners + canvas->ref(); + return canvas; + } + + static void endRecording(JNIEnv* env, jobject, SkPicture* pict) { + pict->endRecording(); + } +}; + +static JNINativeMethod gPictureMethods[] = { + {"getWidth", "()I", (void*) SkPictureGlue::getWidth}, + {"getHeight", "()I", (void*) SkPictureGlue::getHeight}, + {"nativeConstructor", "(I)I", (void*) SkPictureGlue::newPicture}, + {"nativeCreateFromStream", "(Ljava/io/InputStream;[B)I", (void*)SkPictureGlue::deserialize}, + {"nativeBeginRecording", "(III)I", (void*) SkPictureGlue::beginRecording}, + {"nativeEndRecording", "(I)V", (void*) SkPictureGlue::endRecording}, + {"nativeDraw", "(II)V", (void*) SkPictureGlue::draw}, + {"nativeWriteToStream", "(ILjava/io/OutputStream;[B)Z", (void*)SkPictureGlue::serialize}, + {"nativeDestructor","(I)V", (void*) SkPictureGlue::killPicture} +}; + +#include <android_runtime/AndroidRuntime.h> + +#define REG(env, name, array) \ + result = android::AndroidRuntime::registerNativeMethods(env, name, array, \ + SK_ARRAY_COUNT(array)); \ + if (result < 0) return result + +int register_android_graphics_Picture(JNIEnv* env) { + int result; + + REG(env, "android/graphics/Picture", gPictureMethods); + + return result; +} + +} + + diff --git a/core/jni/android/graphics/PorterDuff.cpp b/core/jni/android/graphics/PorterDuff.cpp new file mode 100644 index 0000000..47de601 --- /dev/null +++ b/core/jni/android/graphics/PorterDuff.cpp @@ -0,0 +1,52 @@ +/* libs/android_runtime/android/graphics/PorterDuff.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// This file was generated from the C++ include file: SkPorterDuff.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkPorterDuff.h" + +namespace android { + +class SkPorterDuffGlue { +public: + + static SkXfermode* CreateXfermode(JNIEnv* env, jobject, + SkPorterDuff::Mode mode) { + return SkPorterDuff::CreateXfermode(mode); + } + +}; + +static JNINativeMethod methods[] = { + {"nativeCreateXfermode","(I)I", (void*) SkPorterDuffGlue::CreateXfermode}, +}; + +int register_android_graphics_PorterDuff(JNIEnv* env) { + int result = AndroidRuntime::registerNativeMethods(env, + "android/graphics/PorterDuffXfermode", methods, + sizeof(methods) / sizeof(methods[0])); + return result; +} + +} diff --git a/core/jni/android/graphics/Rasterizer.cpp b/core/jni/android/graphics/Rasterizer.cpp new file mode 100644 index 0000000..db70b57 --- /dev/null +++ b/core/jni/android/graphics/Rasterizer.cpp @@ -0,0 +1,50 @@ +/* libs/android_runtime/android/graphics/Rasterizer.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// This file was generated from the C++ include file: SkRasterizer.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkRasterizer.h" + +namespace android { + +class SkRasterizerGlue { +public: + + static void finalizer(JNIEnv* env, jobject clazz, SkRasterizer* obj) { + obj->safeUnref(); + } + +}; + +static JNINativeMethod methods[] = { + {"finalizer", "(I)V", (void*) SkRasterizerGlue::finalizer} +}; + +int register_android_graphics_Rasterizer(JNIEnv* env) { + int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/Rasterizer", methods, + sizeof(methods) / sizeof(methods[0])); + return result; +} + +} diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp new file mode 100644 index 0000000..00d6cd9 --- /dev/null +++ b/core/jni/android/graphics/Region.cpp @@ -0,0 +1,231 @@ +#include "SkRegion.h" +#include "SkPath.h" +#include "GraphicsJNI.h" + +#include <jni.h> + +static jfieldID gRegion_nativeInstanceFieldID; + +static inline SkRegion* GetSkRegion(JNIEnv* env, jobject regionObject) { + SkRegion* rgn = (SkRegion*)env->GetIntField(regionObject, gRegion_nativeInstanceFieldID); + SkASSERT(rgn != NULL); + return rgn; +} + +static SkRegion* Region_constructor(JNIEnv* env, jobject) { + return new SkRegion; +} + +static void Region_destructor(JNIEnv* env, jobject, SkRegion* region) { + SkASSERT(region); + delete region; +} + +static void Region_setRegion(JNIEnv* env, jobject, SkRegion* dst, const SkRegion* src) { + SkASSERT(dst && src); + *dst = *src; +} + +static jboolean Region_setRect(JNIEnv* env, jobject, SkRegion* dst, int left, int top, int right, int bottom) { + return dst->setRect(left, top, right, bottom); +} + +static jboolean Region_setPath(JNIEnv* env, jobject, SkRegion* dst, + const SkPath* path, const SkRegion* clip) { + SkASSERT(dst && path && clip); + return dst->setPath(*path, *clip); +} + +static jboolean Region_getBounds(JNIEnv* env, jobject, SkRegion* region, jobject rectBounds) { + GraphicsJNI::irect_to_jrect(region->getBounds(), env, rectBounds); + return !region->isEmpty(); +} + +static jboolean Region_getBoundaryPath(JNIEnv* env, jobject, const SkRegion* region, SkPath* path) { + return region->getBoundaryPath(path); +} + +static jboolean Region_op0(JNIEnv* env, jobject, SkRegion* dst, int left, int top, int right, int bottom, int op) { + SkIRect ir; + + ir.set(left, top, right, bottom); + return dst->op(ir, (SkRegion::Op)op); +} + +static jboolean Region_op1(JNIEnv* env, jobject, SkRegion* dst, jobject rectObject, const SkRegion* region, int op) { + SkIRect ir; + GraphicsJNI::jrect_to_irect(env, rectObject, &ir); + return dst->op(ir, *region, (SkRegion::Op)op); +} + +static jboolean Region_op2(JNIEnv* env, jobject, SkRegion* dst, const SkRegion* region1, const SkRegion* region2, int op) { + return dst->op(*region1, *region2, (SkRegion::Op)op); +} + +//////////////////////////////////// These are methods, not static + +static jboolean Region_isEmpty(JNIEnv* env, jobject region) { + return GetSkRegion(env, region)->isEmpty(); +} + +static jboolean Region_isRect(JNIEnv* env, jobject region) { + return GetSkRegion(env, region)->isRect(); +} + +static jboolean Region_isComplex(JNIEnv* env, jobject region) { + return GetSkRegion(env, region)->isComplex(); +} + +static jboolean Region_contains(JNIEnv* env, jobject region, int x, int y) { + return GetSkRegion(env, region)->contains(x, y); +} + +static jboolean Region_quickContains(JNIEnv* env, jobject region, int left, int top, int right, int bottom) { + return GetSkRegion(env, region)->quickContains(left, top, right, bottom); +} + +static jboolean Region_quickRejectIIII(JNIEnv* env, jobject region, int left, int top, int right, int bottom) { + SkIRect ir; + ir.set(left, top, right, bottom); + return GetSkRegion(env, region)->quickReject(ir); +} + +static jboolean Region_quickRejectRgn(JNIEnv* env, jobject region, jobject other) { + return GetSkRegion(env, region)->quickReject(*GetSkRegion(env, other)); +} + +static void Region_translate(JNIEnv* env, jobject region, int x, int y, jobject dst) { + SkRegion* rgn = GetSkRegion(env, region); + if (dst) + rgn->translate(x, y, GetSkRegion(env, dst)); + else + rgn->translate(x, y); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Parcel.h" +#include "android_util_Binder.h" + +static SkRegion* Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel) +{ + if (parcel == NULL) { + return NULL; + } + + android::Parcel* p = android::parcelForJavaObject(env, parcel); + + SkRegion* region = new SkRegion; + size_t size = p->readInt32(); + region->unflatten(p->readInplace(size)); + + return region; +} + +static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, const SkRegion* region, jobject parcel) +{ + if (parcel == NULL) { + return false; + } + + android::Parcel* p = android::parcelForJavaObject(env, parcel); + + size_t size = region->flatten(NULL); + p->writeInt32(size); + region->flatten(p->writeInplace(size)); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct RgnIterPair { + SkRegion fRgn; // a copy of the caller's region + SkRegion::Iterator fIter; // an iterator acting upon the copy (fRgn) + + RgnIterPair(const SkRegion& rgn) : fRgn(rgn) { + // have our iterator reference our copy (fRgn), so we know it will be + // unchanged for the lifetime of the iterator + fIter.reset(fRgn); + } +}; + +static RgnIterPair* RegionIter_constructor(JNIEnv* env, jobject, const SkRegion* region) +{ + SkASSERT(region); + return new RgnIterPair(*region); +} + +static void RegionIter_destructor(JNIEnv* env, jobject, RgnIterPair* pair) +{ + SkASSERT(pair); + delete pair; +} + +static jboolean RegionIter_next(JNIEnv* env, jobject, RgnIterPair* pair, jobject rectObject) +{ + // the caller has checked that rectObject is not nul + SkASSERT(pair); + SkASSERT(rectObject); + + if (!pair->fIter.done()) { + GraphicsJNI::irect_to_jrect(pair->fIter.rect(), env, rectObject); + pair->fIter.next(); + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include <android_runtime/AndroidRuntime.h> + +static JNINativeMethod gRegionIterMethods[] = { + { "nativeConstructor", "(I)I", (void*)RegionIter_constructor }, + { "nativeDestructor", "(I)V", (void*)RegionIter_destructor }, + { "nativeNext", "(ILandroid/graphics/Rect;)Z", (void*)RegionIter_next } +}; + +static JNINativeMethod gRegionMethods[] = { + // these are static methods + { "nativeConstructor", "()I", (void*)Region_constructor }, + { "nativeDestructor", "(I)V", (void*)Region_destructor }, + { "nativeSetRegion", "(II)Z", (void*)Region_setRegion }, + { "nativeSetRect", "(IIIII)Z", (void*)Region_setRect }, + { "nativeSetPath", "(III)Z", (void*)Region_setPath }, + { "nativeGetBounds", "(ILandroid/graphics/Rect;)Z", (void*)Region_getBounds }, + { "nativeGetBoundaryPath", "(II)Z", (void*)Region_getBoundaryPath }, + { "nativeOp", "(IIIIII)Z", (void*)Region_op0 }, + { "nativeOp", "(ILandroid/graphics/Rect;II)Z", (void*)Region_op1 }, + { "nativeOp", "(IIII)Z", (void*)Region_op2 }, + // these are methods that take the java region object + { "isEmpty", "()Z", (void*)Region_isEmpty }, + { "isRect", "()Z", (void*)Region_isRect }, + { "isComplex", "()Z", (void*)Region_isComplex }, + { "contains", "(II)Z", (void*)Region_contains }, + { "quickContains", "(IIII)Z", (void*)Region_quickContains }, + { "quickReject", "(IIII)Z", (void*)Region_quickRejectIIII }, + { "quickReject", "(Landroid/graphics/Region;)Z", (void*)Region_quickRejectRgn }, + { "translate", "(IILandroid/graphics/Region;)V", (void*)Region_translate }, + // parceling methods + { "nativeCreateFromParcel", "(Landroid/os/Parcel;)I", (void*)Region_createFromParcel }, + { "nativeWriteToParcel", "(ILandroid/os/Parcel;)Z", (void*)Region_writeToParcel } +}; + +int register_android_graphics_Region(JNIEnv* env); +int register_android_graphics_Region(JNIEnv* env) +{ + jclass clazz = env->FindClass("android/graphics/Region"); + SkASSERT(clazz); + + gRegion_nativeInstanceFieldID = env->GetFieldID(clazz, "mNativeRegion", "I"); + SkASSERT(gRegion_nativeInstanceFieldID); + + int result = android::AndroidRuntime::registerNativeMethods(env, "android/graphics/Region", + gRegionMethods, SK_ARRAY_COUNT(gRegionMethods)); + if (result < 0) + return result; + + return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/RegionIterator", + gRegionIterMethods, SK_ARRAY_COUNT(gRegionIterMethods)); +} diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp new file mode 100644 index 0000000..71dc875 --- /dev/null +++ b/core/jni/android/graphics/Shader.cpp @@ -0,0 +1,273 @@ +#include <jni.h> +#include "GraphicsJNI.h" + +#include "SkShader.h" +#include "SkGradientShader.h" +#include "SkPorterDuff.h" +#include "SkShaderExtras.h" +#include "SkTemplates.h" +#include "SkXfermode.h" + +static void Color_RGBToHSV(JNIEnv* env, jobject, int red, int green, int blue, jfloatArray hsvArray) +{ + SkScalar hsv[3]; + SkRGBToHSV(red, green, blue, hsv); + + AutoJavaFloatArray autoHSV(env, hsvArray, 3); + float* values = autoHSV.ptr(); + for (int i = 0; i < 3; i++) { + values[i] = SkScalarToFloat(hsv[i]); + } +} + +static int Color_HSVToColor(JNIEnv* env, jobject, int alpha, jfloatArray hsvArray) +{ + AutoJavaFloatArray autoHSV(env, hsvArray, 3); + float* values = autoHSV.ptr();; + SkScalar hsv[3]; + + for (int i = 0; i < 3; i++) { + hsv[i] = SkFloatToScalar(values[i]); + } + + return SkHSVToColor(alpha, hsv); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static void Shader_destructor(JNIEnv* env, jobject, SkShader* shader) +{ + SkASSERT(shader != NULL); + shader->unref(); +} + +static bool Shader_getLocalMatrix(JNIEnv* env, jobject, const SkShader* shader, SkMatrix* matrix) +{ + SkASSERT(shader != NULL); + return shader->getLocalMatrix(matrix); +} + +static void Shader_setLocalMatrix(JNIEnv* env, jobject, SkShader* shader, const SkMatrix* matrix) +{ + SkASSERT(shader != NULL); + + if (NULL == matrix) { + shader->resetLocalMatrix(); + } + else { + shader->setLocalMatrix(*matrix); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static SkShader* BitmapShader_constructor(JNIEnv* env, jobject, const SkBitmap* bitmap, + int tileModeX, int tileModeY) +{ + return SkShader::CreateBitmapShader(*bitmap, + (SkShader::TileMode)tileModeX, + (SkShader::TileMode)tileModeY); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static SkShader* LinearGradient_create1(JNIEnv* env, jobject, + float x0, float y0, float x1, float y1, + jintArray colorArray, jfloatArray posArray, int tileMode) +{ + SkPoint pts[2]; + 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); + + SkAutoSTMalloc<8, SkScalar> storage(posArray ? count : 0); + SkScalar* pos = NULL; + + if (posArray) { + AutoJavaFloatArray autoPos(env, posArray, count); + const float* posValues = autoPos.ptr(); + pos = (SkScalar*)storage.get(); + for (size_t i = 0; i < count; i++) + pos[i] = SkFloatToScalar(posValues[i]); + } + + SkShader* shader = SkGradientShader::CreateLinear(pts, (const SkColor*)colorValues, + pos, count, (SkShader::TileMode)tileMode); + env->ReleaseIntArrayElements(colorArray, colorValues, 0); + return shader; +} + +static SkShader* LinearGradient_create2(JNIEnv* env, jobject, + float x0, float y0, float x1, float y1, + int color0, int color1, int tileMode) +{ + SkPoint pts[2]; + pts[0].set(SkFloatToScalar(x0), SkFloatToScalar(y0)); + pts[1].set(SkFloatToScalar(x1), SkFloatToScalar(y1)); + + SkColor colors[2]; + colors[0] = color0; + colors[1] = color1; + + return SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static SkShader* RadialGradient_create1(JNIEnv* env, jobject, + float x, float y, float radius, + jintArray colorArray, jfloatArray posArray, int tileMode) +{ + SkPoint center; + center.set(SkFloatToScalar(x), SkFloatToScalar(y)); + + size_t count = env->GetArrayLength(colorArray); + int* colorValues = env->GetIntArrayElements(colorArray, NULL); + + SkAutoSTMalloc<8, SkScalar> storage(posArray ? count : 0); + SkScalar* pos = NULL; + + if (posArray) { + AutoJavaFloatArray autoPos(env, posArray, count); + const float* posValues = autoPos.ptr(); + pos = (SkScalar*)storage.get(); + for (size_t i = 0; i < count; i++) + 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); + return shader; +} + +static SkShader* RadialGradient_create2(JNIEnv* env, jobject, + float x, float y, float radius, + int color0, int color1, int tileMode) +{ + SkPoint center; + center.set(SkFloatToScalar(x), SkFloatToScalar(y)); + + SkColor colors[2]; + colors[0] = color0; + colors[1] = color1; + + return SkGradientShader::CreateRadial(center, SkFloatToScalar(radius), colors, NULL, + 2, (SkShader::TileMode)tileMode); +} + +/////////////////////////////////////////////////////////////////////////////// + +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); + + SkAutoSTMalloc<8, SkScalar> storage(jpositions ? count : 0); + SkScalar* pos = NULL; + + if (NULL != jpositions) { + AutoJavaFloatArray autoPos(env, jpositions, count); + const float* posValues = autoPos.ptr(); + pos = (SkScalar*)storage.get(); + 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); + return shader; +} + +static SkShader* SweepGradient_create2(JNIEnv* env, jobject, float x, float y, + int color0, int color1) +{ + SkColor colors[2]; + colors[0] = color0; + colors[1] = color1; + return SkGradientShader::CreateSweep(SkFloatToScalar(x), SkFloatToScalar(y), + colors, NULL, 2); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static SkShader* ComposeShader_create1(JNIEnv* env, jobject, + SkShader* shaderA, SkShader* shaderB, SkXfermode* mode) +{ + return new SkComposeShader(shaderA, shaderB, mode); +} + +static SkShader* ComposeShader_create2(JNIEnv* env, jobject, + SkShader* shaderA, SkShader* shaderB, SkPorterDuff::Mode mode) +{ + SkAutoUnref au(SkPorterDuff::CreateXfermode(mode)); + + return new SkComposeShader(shaderA, shaderB, (SkXfermode*)au.get()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static JNINativeMethod gColorMethods[] = { + { "nativeRGBToHSV", "(III[F)V", (void*)Color_RGBToHSV }, + { "nativeHSVToColor", "(I[F)I", (void*)Color_HSVToColor } +}; + +static JNINativeMethod gShaderMethods[] = { + { "nativeDestructor", "(I)V", (void*)Shader_destructor }, + { "nativeGetLocalMatrix", "(II)Z", (void*)Shader_getLocalMatrix }, + { "nativeSetLocalMatrix", "(II)V", (void*)Shader_setLocalMatrix } +}; + +static JNINativeMethod gBitmapShaderMethods[] = { + { "nativeCreate", "(III)I", (void*)BitmapShader_constructor } +}; + +static JNINativeMethod gLinearGradientMethods[] = { + { "nativeCreate1", "(FFFF[I[FI)I", (void*)LinearGradient_create1 }, + { "nativeCreate2", "(FFFFIII)I", (void*)LinearGradient_create2 } +}; + +static JNINativeMethod gRadialGradientMethods[] = { + {"nativeCreate1", "(FFF[I[FI)I", (void*)RadialGradient_create1 }, + {"nativeCreate2", "(FFFIII)I", (void*)RadialGradient_create2 } +}; + +static JNINativeMethod gSweepGradientMethods[] = { + {"nativeCreate1", "(FF[I[F)I", (void*)SweepGradient_create1 }, + {"nativeCreate2", "(FFII)I", (void*)SweepGradient_create2 } +}; + +static JNINativeMethod gComposeShaderMethods[] = { + {"nativeCreate1", "(III)I", (void*)ComposeShader_create1 }, + {"nativeCreate2", "(III)I", (void*)ComposeShader_create2 } +}; + +#include <android_runtime/AndroidRuntime.h> + +#define REG(env, name, array) \ + result = android::AndroidRuntime::registerNativeMethods(env, name, array, SK_ARRAY_COUNT(array)); \ + if (result < 0) return result + +int register_android_graphics_Shader(JNIEnv* env); +int register_android_graphics_Shader(JNIEnv* env) +{ + int result; + + REG(env, "android/graphics/Color", gColorMethods); + REG(env, "android/graphics/Shader", gShaderMethods); + REG(env, "android/graphics/BitmapShader", gBitmapShaderMethods); + REG(env, "android/graphics/LinearGradient", gLinearGradientMethods); + REG(env, "android/graphics/RadialGradient", gRadialGradientMethods); + REG(env, "android/graphics/SweepGradient", gSweepGradientMethods); + REG(env, "android/graphics/ComposeShader", gComposeShaderMethods); + + return result; +} + diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp new file mode 100644 index 0000000..32954ce --- /dev/null +++ b/core/jni/android/graphics/Typeface.cpp @@ -0,0 +1,156 @@ +#include "jni.h" +#include <android_runtime/AndroidRuntime.h> + +#include "GraphicsJNI.h" +#include <android_runtime/android_util_AssetManager.h> +#include "SkStream.h" +#include "SkTypeface.h" +#include <utils/AssetManager.h> + +using namespace android; + +class AutoJavaStringToUTF8 { +public: + AutoJavaStringToUTF8(JNIEnv* env, jstring str) : fEnv(env), fJStr(str) + { + fCStr = env->GetStringUTFChars(str, NULL); + } + ~AutoJavaStringToUTF8() + { + fEnv->ReleaseStringUTFChars(fJStr, fCStr); + } + const char* c_str() const { return fCStr; } + +private: + JNIEnv* fEnv; + jstring fJStr; + const char* fCStr; +}; + +static SkTypeface* Typeface_create(JNIEnv* env, jobject, jstring name, + SkTypeface::Style style) { + SkTypeface* face; + + if (NULL == name) { + face = SkTypeface::Create(NULL, (SkTypeface::Style)style); + } + else { + AutoJavaStringToUTF8 str(env, name); + face = SkTypeface::Create(str.c_str(), style); + } + return face; +} + +static SkTypeface* Typeface_createFromTypeface(JNIEnv* env, jobject, SkTypeface* family, int style) { + return SkTypeface::CreateFromTypeface(family, (SkTypeface::Style)style); +} + +static void Typeface_unref(JNIEnv* env, jobject obj, SkTypeface* face) { + face->unref(); +} + +static int Typeface_getStyle(JNIEnv* env, jobject obj, SkTypeface* face) { + return face->getStyle(); +} + +class AssetStream : public SkStream { +public: + AssetStream(Asset* asset, bool hasMemoryBase) : fAsset(asset) + { + fMemoryBase = hasMemoryBase ? fAsset->getBuffer(false) : NULL; + } + + virtual ~AssetStream() + { + delete fAsset; + } + + virtual const void* getMemoryBase() + { + return fMemoryBase; + } + + virtual bool rewind() + { + off_t pos = fAsset->seek(0, SEEK_SET); + return pos != (off_t)-1; + } + + virtual size_t read(void* buffer, size_t size) + { + ssize_t amount; + + if (NULL == buffer) + { + if (0 == size) // caller is asking us for our total length + return fAsset->getLength(); + + // asset->seek returns new total offset + // we want to return amount that was skipped + + off_t oldOffset = fAsset->seek(0, SEEK_CUR); + if (-1 == oldOffset) + return 0; + off_t newOffset = fAsset->seek(size, SEEK_CUR); + if (-1 == newOffset) + return 0; + + amount = newOffset - oldOffset; + } + else + { + amount = fAsset->read(buffer, size); + } + + if (amount < 0) + amount = 0; + return amount; + } + +private: + Asset* fAsset; + const void* fMemoryBase; +}; + +static SkTypeface* Typeface_createFromAsset(JNIEnv* env, jobject, + jobject jassetMgr, + jstring jpath) { + + NPE_CHECK_RETURN_ZERO(env, jassetMgr); + NPE_CHECK_RETURN_ZERO(env, jpath); + + AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); + if (NULL == mgr) { + return NULL; + } + + AutoJavaStringToUTF8 str(env, jpath); + Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + if (NULL == asset) { + return NULL; + } + + return SkTypeface::CreateFromStream(new AssetStream(asset, true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +static JNINativeMethod gTypefaceMethods[] = { + { "nativeCreate", "(Ljava/lang/String;I)I", (void*)Typeface_create }, + { "nativeCreateFromTypeface", "(II)I", (void*)Typeface_createFromTypeface }, + { "nativeUnref", "(I)V", (void*)Typeface_unref }, + { "nativeGetStyle", "(I)I", (void*)Typeface_getStyle }, + { "nativeCreateFromAsset", + "(Landroid/content/res/AssetManager;Ljava/lang/String;)I", + (void*)Typeface_createFromAsset } +}; + +int register_android_graphics_Typeface(JNIEnv* env); +int register_android_graphics_Typeface(JNIEnv* env) +{ + return android::AndroidRuntime::registerNativeMethods(env, + "android/graphics/Typeface", + gTypefaceMethods, + SK_ARRAY_COUNT(gTypefaceMethods)); +} + diff --git a/core/jni/android/graphics/Xfermode.cpp b/core/jni/android/graphics/Xfermode.cpp new file mode 100644 index 0000000..2b53d28 --- /dev/null +++ b/core/jni/android/graphics/Xfermode.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkAvoidXfermode.h" +#include "SkPixelXorXfermode.h" + +namespace android { + +class SkXfermodeGlue { +public: + + static void finalizer(JNIEnv* env, jobject, SkXfermode* obj) + { + obj->safeUnref(); + } + + static SkXfermode* avoid_create(JNIEnv* env, jobject, SkColor opColor, + U8CPU tolerance, SkAvoidXfermode::Mode mode) + { + return new SkAvoidXfermode(opColor, tolerance, mode); + } + + static SkXfermode* pixelxor_create(JNIEnv* env, jobject, SkColor opColor) + { + return new SkPixelXorXfermode(opColor); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static JNINativeMethod gXfermodeMethods[] = { + {"finalizer", "(I)V", (void*) SkXfermodeGlue::finalizer} +}; + +static JNINativeMethod gAvoidMethods[] = { + {"nativeCreate", "(III)I", (void*) SkXfermodeGlue::avoid_create} +}; + +static JNINativeMethod gPixelXorMethods[] = { + {"nativeCreate", "(I)I", (void*) SkXfermodeGlue::pixelxor_create} +}; + +#include <android_runtime/AndroidRuntime.h> + +#define REG(env, name, array) \ + result = android::AndroidRuntime::registerNativeMethods(env, name, array, \ + SK_ARRAY_COUNT(array)); \ + if (result < 0) return result + +int register_android_graphics_Xfermode(JNIEnv* env) { + int result; + + REG(env, "android/graphics/Xfermode", gXfermodeMethods); + REG(env, "android/graphics/AvoidXfermode", gAvoidMethods); + REG(env, "android/graphics/PixelXorXfermode", gPixelXorMethods); + + return 0; +} + +} diff --git a/core/jni/android/opengl/poly.h b/core/jni/android/opengl/poly.h new file mode 100644 index 0000000..85b44e3 --- /dev/null +++ b/core/jni/android/opengl/poly.h @@ -0,0 +1,51 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** 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. +*/ + +/* Based on the public domain code: + * Generic Convex Polygon Scan Conversion and Clipping + * by Paul Heckbert + * from "Graphics Gems", Academic Press, 1990 + */ + + +#ifndef POLY_HDR +#define POLY_HDR + +namespace android { + +#define POLY_NMAX 10 /* max #sides to a polygon; change if needed */ +/* note that poly_clip, given an n-gon as input, might output an (n+6)gon */ +/* POLY_NMAX=10 is thus appropriate if input polygons are triangles or quads */ + +typedef struct { /* A POLYGON VERTEX */ + float sx, sy, sz, sw; /* screen space position (sometimes homo.) */ +} Poly_vert; + +typedef struct { /* A POLYGON */ + int n; /* number of sides */ + Poly_vert vert[POLY_NMAX]; /* vertices */ +} Poly; + +#define POLY_CLIP_OUT 0 /* polygon entirely outside box */ +#define POLY_CLIP_PARTIAL 1 /* polygon partially inside */ +#define POLY_CLIP_IN 2 /* polygon entirely inside box */ + +int poly_clip_to_frustum(Poly *p1); + +} // namespace android + +#endif diff --git a/core/jni/android/opengl/poly_clip.cpp b/core/jni/android/opengl/poly_clip.cpp new file mode 100644 index 0000000..04e4b17 --- /dev/null +++ b/core/jni/android/opengl/poly_clip.cpp @@ -0,0 +1,155 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** 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. +*/ + +/* + * Generic Convex Polygon Scan Conversion and Clipping + * by Paul Heckbert + * from "Graphics Gems", Academic Press, 1990 + */ + +/* Based on the public domain code: + * poly_clip.c: homogeneous 3-D convex polygon clipper + * + * Paul Heckbert 1985, Dec 1989 + */ + +#include "poly.h" +#include "string.h" + +#define LOG_TAG "StreetView" +#include <utils/Log.h> + +namespace android { + +#define SWAP(a, b, temp) {temp = a; a = b; b = temp;} +#define COORD(vert, i) ((float *)(vert))[i] + +#define CLIP_AND_SWAP(elem, sign, k, p, q, r) { \ + poly_clip_to_halfspace(p, q, &v->elem-(float *)v, sign, sign*k); \ + if (q->n==0) {p1->n = 0; return POLY_CLIP_OUT;} \ + SWAP(p, q, r); \ +} + +/* + * poly_clip_to_halfspace: clip convex polygon p against a plane, + * copying the portion satisfying sign*s[index] < k*sw into q, + * where s is a Poly_vert* cast as a float*. + * index is an index into the array of floats at each vertex, such that + * s[index] is sx, sy, or sz (screen space x, y, or z). + * Thus, to clip against xmin, use + * poly_clip_to_halfspace(p, q, XINDEX, -1., -xmin); + * and to clip against xmax, use + * poly_clip_to_halfspace(p, q, XINDEX, 1., xmax); + */ + +void poly_clip_to_halfspace(Poly* p, Poly* q, int index, float sign, float k) +{ + unsigned long m; + float *up, *vp, *wp; + Poly_vert *v; + int i; + Poly_vert *u; + float t, tu, tv; + + q->n = 0; + + /* start with u=vert[n-1], v=vert[0] */ + u = &p->vert[p->n-1]; + tu = sign*COORD(u, index) - u->sw*k; + for (v= &p->vert[0], i=p->n; i>0; i--, u=v, tu=tv, v++) { + /* on old polygon (p), u is previous vertex, v is current vertex */ + /* tv is negative if vertex v is in */ + tv = sign*COORD(v, index) - v->sw*k; + if ((tu <= 0.0f) ^ (tv <= 0.0f)) { + /* edge crosses plane; add intersection point to q */ + t = tu/(tu-tv); + up = (float *)u; + vp = (float *)v; + wp = (float *)&q->vert[q->n].sx; + for(int i = 0; i < 4; i++, wp++, up++, vp++) { + *wp = *up+t*(*vp-*up); + } + q->n++; + } + if (tv<=0.0f) /* vertex v is in, copy it to q */ + q->vert[q->n++] = *v; + } +} + +/* + * poly_clip_to_frustum: Clip the convex polygon p1 to the screen space frustum + * using the homogeneous screen coordinates (sx, sy, sz, sw) of each vertex, + * testing if v->sx/v->sw > box->x0 and v->sx/v->sw < box->x1, + * and similar tests for y and z, for each vertex v of the polygon. + * If polygon is entirely inside box, then POLY_CLIP_IN is returned. + * If polygon is entirely outside box, then POLY_CLIP_OUT is returned. + * Otherwise, if the polygon is cut by the box, p1 is modified and + * POLY_CLIP_PARTIAL is returned. + * + * Given an n-gon as input, clipping against 6 planes could generate an + * (n+6)gon, so POLY_NMAX in poly.h must be big enough to allow that. + */ + +int poly_clip_to_frustum(Poly *p1) +{ + int x0out = 0, x1out = 0, y0out = 0, y1out = 0, z0out = 0, z1out = 0; + int i; + Poly_vert *v; + Poly p2, *p, *q, *r; + + /* count vertices "outside" with respect to each of the six planes */ + for (v=p1->vert, i=p1->n; i>0; i--, v++) { + float sw = v->sw; + if (v->sx < -sw) x0out++; /* out on left */ + if (v->sx > sw) x1out++; /* out on right */ + if (v->sy < -sw) y0out++; /* out on top */ + if (v->sy > sw) y1out++; /* out on bottom */ + if (v->sz < -sw) z0out++; /* out on near */ + if (v->sz > sw) z1out++; /* out on far */ + } + + /* check if all vertices inside */ + if (x0out+x1out+y0out+y1out+z0out+z1out == 0) + return POLY_CLIP_IN; + + /* check if all vertices are "outside" any of the six planes */ + if (x0out==p1->n || x1out==p1->n || y0out==p1->n || + y1out==p1->n || z0out==p1->n || z1out==p1->n) { + p1->n = 0; + return POLY_CLIP_OUT; + } + + /* + * now clip against each of the planes that might cut the polygon, + * at each step toggling between polygons p1 and p2 + */ + p = p1; + q = &p2; + if (x0out) CLIP_AND_SWAP(sx, -1.0f, -1.0f, p, q, r); + if (x1out) CLIP_AND_SWAP(sx, 1.0f, 1.0f, p, q, r); + if (y0out) CLIP_AND_SWAP(sy, -1.0f, -1.0f, p, q, r); + if (y1out) CLIP_AND_SWAP(sy, 1.0f, 1.0f, p, q, r); + if (z0out) CLIP_AND_SWAP(sz, -1.0f, -1.0f, p, q, r); + if (z1out) CLIP_AND_SWAP(sz, 1.0f, 1.0f, p, q, r); + + /* if result ended up in p2 then copy it to p1 */ + if (p==&p2) + memcpy(p1, &p2, sizeof(Poly)-(POLY_NMAX-p2.n)*sizeof(Poly_vert)); + return POLY_CLIP_PARTIAL; +} + +} // namespace android diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp new file mode 100644 index 0000000..5cd2ceb --- /dev/null +++ b/core/jni/android/opengl/util.cpp @@ -0,0 +1,730 @@ +/** + ** Copyright 2007, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** 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. + */ + +#include <nativehelper/jni.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <dlfcn.h> + +#include <GLES/gl.h> + +#include <graphics/SkBitmap.h> + +#include "android_runtime/AndroidRuntime.h" + +#undef LOG_TAG +#define LOG_TAG "OpenGLUtil" +#include <utils/Log.h> +#include "utils/misc.h" + +#include "poly.h" + +namespace android { + +static jclass gIAEClass; +static jclass gUOEClass; + +static inline +void mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) { + pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w; + pDest[1] = pM[1 + 4 * 0] * x + pM[1 + 4 * 1] * y + pM[1 + 4 * 2] * z + pM[1 + 4 * 3] * w; + pDest[2] = pM[2 + 4 * 0] * x + pM[2 + 4 * 1] * y + pM[2 + 4 * 2] * z + pM[2 + 4 * 3] * w; + pDest[3] = pM[3 + 4 * 0] * x + pM[3 + 4 * 1] * y + pM[3 + 4 * 2] * z + pM[3 + 4 * 3] * w; +} + +class MallocHelper { +public: + MallocHelper() { + mData = 0; + } + + ~MallocHelper() { + if (mData != 0) { + free(mData); + } + } + + void* alloc(size_t size) { + mData = malloc(size); + return mData; + } + +private: + void* mData; +}; + +#if 0 +static +void +print_poly(const char* label, Poly* pPoly) { + LOGI("%s: %d verts", label, pPoly->n); + for(int i = 0; i < pPoly->n; i++) { + Poly_vert* pV = & pPoly->vert[i]; + LOGI("[%d] %g, %g, %g %g", i, pV->sx, pV->sy, pV->sz, pV->sw); + } +} +#endif + +static +int visibilityTest(float* pWS, float* pPositions, int positionsLength, + unsigned short* pIndices, int indexCount) { + MallocHelper mallocHelper; + int result = POLY_CLIP_OUT; + float* pTransformed = 0; + int transformedIndexCount = 0; + + if ( indexCount < 3 ) { + return POLY_CLIP_OUT; + } + + // Find out how many vertices we need to transform + // We transform every vertex between the min and max indices, inclusive. + // This is OK for the data sets we expect to use with this function, but + // for other loads it might be better to use a more sophisticated vertex + // cache of some sort. + + int minIndex = 65536; + int maxIndex = -1; + for(int i = 0; i < indexCount; i++) { + int index = pIndices[i]; + if ( index < minIndex ) { + minIndex = index; + } + if ( index > maxIndex ) { + maxIndex = index; + } + } + + if ( maxIndex * 3 > positionsLength) { + return -1; + } + + transformedIndexCount = maxIndex - minIndex + 1; + pTransformed = (float*) mallocHelper.alloc(transformedIndexCount * 4 * sizeof(float)); + + if (pTransformed == 0 ) { + return -2; + } + + // Transform the vertices + { + const float* pSrc = pPositions + 3 * minIndex; + float* pDst = pTransformed; + for (int i = 0; i < transformedIndexCount; i++, pSrc += 3, pDst += 4) { + mx4transform(pSrc[0], pSrc[1], pSrc[2], 1.0f, pWS, pDst); + } + } + + // Clip the triangles + + Poly poly; + float* pDest = & poly.vert[0].sx; + for (int i = 0; i < indexCount; i += 3) { + poly.n = 3; + memcpy(pDest , pTransformed + 4 * (pIndices[i ] - minIndex), 4 * sizeof(float)); + memcpy(pDest + 4, pTransformed + 4 * (pIndices[i + 1] - minIndex), 4 * sizeof(float)); + memcpy(pDest + 8, pTransformed + 4 * (pIndices[i + 2] - minIndex), 4 * sizeof(float)); + result = poly_clip_to_frustum(&poly); + if ( result != POLY_CLIP_OUT) { + return result; + } + } + + return result; +} + +template<class JArray, class T> +class ArrayHelper { +public: + ArrayHelper(JNIEnv* env, JArray ref, jint offset, jint minSize) { + mEnv = env; + mRef = ref; + mOffset = offset; + mMinSize = minSize; + mBase = 0; + mReleaseParam = JNI_ABORT; + } + + ~ArrayHelper() { + if (mBase) { + mEnv->ReleasePrimitiveArrayCritical(mRef, mBase, mReleaseParam); + } + } + + // We seperate the bounds check from the initialization because we want to + // be able to bounds-check multiple arrays, and we can't throw an exception + // after we've called GetPrimitiveArrayCritical. + + // Return true if the bounds check succeeded + // Else instruct the runtime to throw an exception + + bool check() { + if ( ! mRef) { + mEnv->ThrowNew(gIAEClass, "array == null"); + return false; + } + if ( mOffset < 0) { + mEnv->ThrowNew(gIAEClass, "offset < 0"); + return false; + } + mLength = mEnv->GetArrayLength(mRef) - mOffset; + if (mLength < mMinSize ) { + mEnv->ThrowNew(gIAEClass, "length - offset < n"); + return false; + } + return true; + } + + // Bind the array. + + void bind() { + mBase = (T*) mEnv->GetPrimitiveArrayCritical(mRef, (jboolean *) 0); + mData = mBase + mOffset; + } + + void commitChanges() { + mReleaseParam = 0; + } + + T* mData; + int mLength; + +private: + T* mBase; + JNIEnv* mEnv; + JArray mRef; + jint mOffset; + jint mMinSize; + int mReleaseParam; +}; + +typedef ArrayHelper<jfloatArray, float> FloatArrayHelper; +typedef ArrayHelper<jcharArray, unsigned short> UnsignedShortArrayHelper; +typedef ArrayHelper<jintArray, int> IntArrayHelper; +typedef ArrayHelper<jbyteArray, unsigned char> ByteArrayHelper; + +inline float distance2(float x, float y, float z) { + return x * x + y * y + z * z; +} + +inline float distance(float x, float y, float z) { + return sqrtf(distance2(x, y, z)); +} + +static +void util_computeBoundingSphere(JNIEnv *env, jclass clazz, + jfloatArray positions_ref, jint positionsOffset, jint positionsCount, + jfloatArray sphere_ref, jint sphereOffset) { + FloatArrayHelper positions(env, positions_ref, positionsOffset, 0); + FloatArrayHelper sphere(env, sphere_ref, sphereOffset, 4); + + bool checkOK = positions.check() && sphere.check(); + if (! checkOK) { + return; + } + + positions.bind(); + sphere.bind(); + + if ( positionsCount < 1 ) { + env->ThrowNew(gIAEClass, "positionsCount < 1"); + return; + } + + const float* pSrc = positions.mData; + + // find bounding box + float x0 = *pSrc++; + float x1 = x0; + float y0 = *pSrc++; + float y1 = y0; + float z0 = *pSrc++; + float z1 = z0; + + for(int i = 1; i < positionsCount; i++) { + { + float x = *pSrc++; + if (x < x0) { + x0 = x; + } + else if (x > x1) { + x1 = x; + } + } + { + float y = *pSrc++; + if (y < y0) { + y0 = y; + } + else if (y > y1) { + y1 = y; + } + } + { + float z = *pSrc++; + if (z < z0) { + z0 = z; + } + else if (z > z1) { + z1 = z; + } + } + } + + // Because we know our input meshes fit pretty well into bounding boxes, + // just take the diagonal of the box as defining our sphere. + float* pSphere = sphere.mData; + float dx = x1 - x0; + float dy = y1 - y0; + float dz = z1 - z0; + *pSphere++ = x0 + dx * 0.5f; + *pSphere++ = y0 + dy * 0.5f; + *pSphere++ = z0 + dz * 0.5f; + *pSphere++ = distance(dx, dy, dz) * 0.5f; + + sphere.commitChanges(); +} + +static void normalizePlane(float* p) { + float rdist = 1.0f / distance(p[0], p[1], p[2]); + for(int i = 0; i < 4; i++) { + p[i] *= rdist; + } +} + +static inline float dot3(float x0, float y0, float z0, float x1, float y1, float z1) { + return x0 * x1 + y0 * y1 + z0 * z1; +} + +static inline float signedDistance(const float* pPlane, float x, float y, float z) { + return dot3(pPlane[0], pPlane[1], pPlane[2], x, y, z) + pPlane[3]; +} + +// Return true if the sphere intersects or is inside the frustum + +static bool sphereHitsFrustum(const float* pFrustum, const float* pSphere) { + float x = pSphere[0]; + float y = pSphere[1]; + float z = pSphere[2]; + float negRadius = -pSphere[3]; + for (int i = 0; i < 6; i++, pFrustum += 4) { + if (signedDistance(pFrustum, x, y, z) <= negRadius) { + return false; + } + } + return true; +} + +static void computeFrustum(const float* m, float* f) { + float m3 = m[3]; + float m7 = m[7]; + float m11 = m[11]; + float m15 = m[15]; + // right + f[0] = m3 - m[0]; + f[1] = m7 - m[4]; + f[2] = m11 - m[8]; + f[3] = m15 - m[12]; + normalizePlane(f); + f+= 4; + + // left + f[0] = m3 + m[0]; + f[1] = m7 + m[4]; + f[2] = m11 + m[8]; + f[3] = m15 + m[12]; + normalizePlane(f); + f+= 4; + + // top + f[0] = m3 - m[1]; + f[1] = m7 - m[5]; + f[2] = m11 - m[9]; + f[3] = m15 - m[13]; + normalizePlane(f); + f+= 4; + + // bottom + f[0] = m3 + m[1]; + f[1] = m7 + m[5]; + f[2] = m11 + m[9]; + f[3] = m15 + m[13]; + normalizePlane(f); + f+= 4; + + // far + f[0] = m3 - m[2]; + f[1] = m7 - m[6]; + f[2] = m11 - m[10]; + f[3] = m15 - m[14]; + normalizePlane(f); + f+= 4; + + // near + f[0] = m3 + m[2]; + f[1] = m7 + m[6]; + f[2] = m11 + m[10]; + f[3] = m15 + m[14]; + normalizePlane(f); +} + +static +int util_frustumCullSpheres(JNIEnv *env, jclass clazz, + jfloatArray mvp_ref, jint mvpOffset, + jfloatArray spheres_ref, jint spheresOffset, jint spheresCount, + jintArray results_ref, jint resultsOffset, jint resultsCapacity) { + float frustum[6*4]; + int outputCount; + int* pResults; + float* pSphere; + FloatArrayHelper mvp(env, mvp_ref, mvpOffset, 16); + FloatArrayHelper spheres(env, spheres_ref, spheresOffset, spheresCount * 4); + IntArrayHelper results(env, results_ref, resultsOffset, resultsCapacity); + + bool initializedOK = mvp.check() && spheres.check() && results.check(); + if (! initializedOK) { + return -1; + } + + mvp.bind(); + spheres.bind(); + results.bind(); + + computeFrustum(mvp.mData, frustum); + + // Cull the spheres + + pSphere = spheres.mData; + pResults = results.mData; + outputCount = 0; + for(int i = 0; i < spheresCount; i++, pSphere += 4) { + if (sphereHitsFrustum(frustum, pSphere)) { + if (outputCount < resultsCapacity) { + *pResults++ = i; + } + outputCount++; + } + } + results.commitChanges(); + return outputCount; +} + +/* + public native int visibilityTest(float[] ws, int wsOffset, + float[] positions, int positionsOffset, + char[] indices, int indicesOffset, int indexCount); + */ + +static +int util_visibilityTest(JNIEnv *env, jclass clazz, + jfloatArray ws_ref, jint wsOffset, + jfloatArray positions_ref, jint positionsOffset, + jcharArray indices_ref, jint indicesOffset, jint indexCount) { + + FloatArrayHelper ws(env, ws_ref, wsOffset, 16); + FloatArrayHelper positions(env, positions_ref, positionsOffset, 0); + UnsignedShortArrayHelper indices(env, indices_ref, indicesOffset, 0); + + bool checkOK = ws.check() && positions.check() && indices.check(); + if (! checkOK) { + // Return value will be ignored, because an exception has been thrown. + return -1; + } + + if (indices.mLength < indexCount) { + env->ThrowNew(gIAEClass, "length < offset + indexCount"); + // Return value will be ignored, because an exception has been thrown. + return -1; + } + + ws.bind(); + positions.bind(); + indices.bind(); + + return visibilityTest(ws.mData, + positions.mData, positions.mLength, + indices.mData, indexCount); +} + +#define I(_i, _j) ((_j)+ 4*(_i)) + +static +void multiplyMM(float* r, const float* lhs, const float* rhs) +{ + for (int i=0 ; i<4 ; i++) { + register const float rhs_i0 = rhs[ I(i,0) ]; + register float ri0 = lhs[ I(0,0) ] * rhs_i0; + register float ri1 = lhs[ I(0,1) ] * rhs_i0; + register float ri2 = lhs[ I(0,2) ] * rhs_i0; + register float ri3 = lhs[ I(0,3) ] * rhs_i0; + for (int j=1 ; j<4 ; j++) { + register const float rhs_ij = rhs[ I(i,j) ]; + ri0 += lhs[ I(j,0) ] * rhs_ij; + ri1 += lhs[ I(j,1) ] * rhs_ij; + ri2 += lhs[ I(j,2) ] * rhs_ij; + ri3 += lhs[ I(j,3) ] * rhs_ij; + } + r[ I(i,0) ] = ri0; + r[ I(i,1) ] = ri1; + r[ I(i,2) ] = ri2; + r[ I(i,3) ] = ri3; + } +} + +static +void util_multiplyMM(JNIEnv *env, jclass clazz, + jfloatArray result_ref, jint resultOffset, + jfloatArray lhs_ref, jint lhsOffset, + jfloatArray rhs_ref, jint rhsOffset) { + + FloatArrayHelper resultMat(env, result_ref, resultOffset, 16); + FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16); + FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 16); + + bool checkOK = resultMat.check() && lhs.check() && rhs.check(); + + if ( !checkOK ) { + return; + } + + resultMat.bind(); + lhs.bind(); + rhs.bind(); + + multiplyMM(resultMat.mData, lhs.mData, rhs.mData); + + resultMat.commitChanges(); +} + +static +void multiplyMV(float* r, const float* lhs, const float* rhs) +{ + mx4transform(rhs[0], rhs[1], rhs[2], rhs[3], lhs, r); +} + +static +void util_multiplyMV(JNIEnv *env, jclass clazz, + jfloatArray result_ref, jint resultOffset, + jfloatArray lhs_ref, jint lhsOffset, + jfloatArray rhs_ref, jint rhsOffset) { + + FloatArrayHelper resultV(env, result_ref, resultOffset, 4); + FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16); + FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 4); + + bool checkOK = resultV.check() && lhs.check() && rhs.check(); + + if ( !checkOK ) { + return; + } + + resultV.bind(); + lhs.bind(); + rhs.bind(); + + multiplyMV(resultV.mData, lhs.mData, rhs.mData); + + resultV.commitChanges(); +} + +// --------------------------------------------------------------------------- + +static jfieldID nativeBitmapID = 0; + +void nativeUtilsClassInit(JNIEnv *env, jclass clazz) +{ + jclass bitmapClass = env->FindClass("android/graphics/Bitmap"); + nativeBitmapID = env->GetFieldID(bitmapClass, "mNativeBitmap", "I"); +} + +static int checkFormat(SkBitmap::Config config, int format, int type) +{ + switch(config) { + case SkBitmap::kIndex8_Config: + if (format == GL_PALETTE8_RGBA8_OES) + return 0; + case SkBitmap::kARGB_8888_Config: + case SkBitmap::kA8_Config: + if (type == GL_UNSIGNED_BYTE) + return 0; + case SkBitmap::kARGB_4444_Config: + case SkBitmap::kRGB_565_Config: + switch (type) { + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_5_5_5_1: + return 0; + case GL_UNSIGNED_BYTE: + if (format == GL_LUMINANCE_ALPHA) + return 0; + } + break; + default: + break; + } + return -1; +} + +static int getInternalFormat(SkBitmap::Config config) +{ + switch(config) { + case SkBitmap::kA8_Config: + return GL_ALPHA; + case SkBitmap::kARGB_4444_Config: + return GL_RGBA; + case SkBitmap::kARGB_8888_Config: + return GL_RGBA; + case SkBitmap::kIndex8_Config: + return GL_PALETTE8_RGBA8_OES; + case SkBitmap::kRGB_565_Config: + return GL_RGB; + default: + return -1; + } +} + +static jint util_texImage2D(JNIEnv *env, jclass clazz, + jint target, jint level, jint internalformat, + jobject jbitmap, jint type, jint border) +{ + SkBitmap const * nativeBitmap = + (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); + const SkBitmap& bitmap(*nativeBitmap); + SkBitmap::Config config = bitmap.getConfig(); + if (internalformat < 0) { + internalformat = getInternalFormat(config); + } + int err = checkFormat(config, internalformat, type); + if (err) + return err; + bitmap.lockPixels(); + const int w = bitmap.width(); + const int h = bitmap.height(); + const void* p = bitmap.getPixels(); + if (internalformat == GL_PALETTE8_RGBA8_OES) { + if (sizeof(SkPMColor) != sizeof(uint32_t)) { + err = -1; + goto error; + } + const size_t size = bitmap.getSize(); + const size_t palette_size = 256*sizeof(SkPMColor); + void* const data = malloc(size + palette_size); + if (data) { + void* const pixels = (char*)data + palette_size; + SkColorTable* ctable = bitmap.getColorTable(); + memcpy(data, ctable->lockColors(), ctable->count() * sizeof(SkPMColor)); + memcpy(pixels, p, size); + ctable->unlockColors(false); + glCompressedTexImage2D(target, level, internalformat, w, h, border, 0, data); + free(data); + } else { + err = -1; + } + } else { + glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p); + } +error: + bitmap.unlockPixels(); + return err; +} + +static jint util_texSubImage2D(JNIEnv *env, jclass clazz, + jint target, jint level, jint xoffset, jint yoffset, + jobject jbitmap, jint format, jint type) +{ + SkBitmap const * nativeBitmap = + (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); + const SkBitmap& bitmap(*nativeBitmap); + SkBitmap::Config config = bitmap.getConfig(); + if (format < 0) { + format = getInternalFormat(config); + if (format == GL_PALETTE8_RGBA8_OES) + return -1; // glCompressedTexSubImage2D() not supported + } + int err = checkFormat(config, format, type); + if (err) + return err; + bitmap.lockPixels(); + const int w = bitmap.width(); + const int h = bitmap.height(); + const void* p = bitmap.getPixels(); + glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p); + bitmap.unlockPixels(); + return 0; +} + +/* + * JNI registration + */ + +static void +lookupClasses(JNIEnv* env) { + gIAEClass = (jclass) env->NewGlobalRef( + env->FindClass("java/lang/IllegalArgumentException")); + gUOEClass = (jclass) env->NewGlobalRef( + env->FindClass("java/lang/UnsupportedOperationException")); +} + +static JNINativeMethod gMatrixMethods[] = { + { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM }, + { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV }, +}; + +static JNINativeMethod gVisiblityMethods[] = { + { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere }, + { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres }, + { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest }, +}; + +static JNINativeMethod gUtilsMethods[] = { + {"nativeClassInit", "()V", (void*)nativeUtilsClassInit }, + { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D }, + { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D }, +}; + +typedef struct _ClassRegistrationInfo { + const char* classPath; + JNINativeMethod* methods; + size_t methodCount; +} ClassRegistrationInfo; + +static ClassRegistrationInfo gClasses[] = { + {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)}, + {"android/opengl/Visibility", gVisiblityMethods, NELEM(gVisiblityMethods)}, + {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)}, +}; + +int register_android_opengl_classes(JNIEnv* env) +{ + lookupClasses(env); + int result = 0; + for (int i = 0; i < NELEM(gClasses); i++) { + ClassRegistrationInfo* cri = &gClasses[i]; + result = AndroidRuntime::registerNativeMethods(env, + cri->classPath, cri->methods, cri->methodCount); + if (result < 0) { + LOGE("Failed to register %s: %d", cri->classPath, result); + break; + } + } + return result; +} + +} // namespace android + diff --git a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp new file mode 100755 index 0000000..7f87d80 --- /dev/null +++ b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp @@ -0,0 +1,553 @@ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "BluetoothAudioGateway.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" + +#define USE_ACCEPT_DIRECTLY (0) +#define USE_SELECT (0) /* 1 for select(), 0 for poll(); used only when + USE_ACCEPT_DIRECTLY == 0 */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/uio.h> +#include <ctype.h> + +#if USE_SELECT +#include <sys/select.h> +#else +#include <sys/poll.h> +#endif + +#ifdef HAVE_BLUETOOTH +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/rfcomm.h> +#include <bluetooth/sco.h> +#endif + +namespace android { + +#ifdef HAVE_BLUETOOTH +static jfieldID field_mNativeData; + /* in */ +static jfieldID field_mHandsfreeAgRfcommChannel; +static jfieldID field_mHeadsetAgRfcommChannel; + /* out */ +static jfieldID field_mTimeoutRemainingMs; /* out */ + +static jfieldID field_mConnectingHeadsetAddress; +static jfieldID field_mConnectingHeadsetRfcommChannel; /* -1 when not connected */ +static jfieldID field_mConnectingHeadsetSocketFd; + +static jfieldID field_mConnectingHandsfreeAddress; +static jfieldID field_mConnectingHandsfreeRfcommChannel; /* -1 when not connected */ +static jfieldID field_mConnectingHandsfreeSocketFd; + + +typedef struct { + int hcidev; + int hf_ag_rfcomm_channel; + int hs_ag_rfcomm_channel; + int hf_ag_rfcomm_sock; + int hs_ag_rfcomm_sock; +} native_data_t; + +static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { + return (native_data_t *)(env->GetIntField(object, + field_mNativeData)); +} + +static int setup_listening_socket(int dev, int channel); +#endif + +static void classInitNative(JNIEnv* env, jclass clazz) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + + /* in */ + field_mNativeData = get_field(env, clazz, "mNativeData", "I"); + field_mHandsfreeAgRfcommChannel = + get_field(env, clazz, "mHandsfreeAgRfcommChannel", "I"); + field_mHeadsetAgRfcommChannel = + get_field(env, clazz, "mHeadsetAgRfcommChannel", "I"); + + /* out */ + field_mConnectingHeadsetAddress = + get_field(env, clazz, + "mConnectingHeadsetAddress", "Ljava/lang/String;"); + field_mConnectingHeadsetRfcommChannel = + get_field(env, clazz, "mConnectingHeadsetRfcommChannel", "I"); + field_mConnectingHeadsetSocketFd = + get_field(env, clazz, "mConnectingHeadsetSocketFd", "I"); + + field_mConnectingHandsfreeAddress = + get_field(env, clazz, + "mConnectingHandsfreeAddress", "Ljava/lang/String;"); + field_mConnectingHandsfreeRfcommChannel = + get_field(env, clazz, "mConnectingHandsfreeRfcommChannel", "I"); + field_mConnectingHandsfreeSocketFd = + get_field(env, clazz, "mConnectingHandsfreeSocketFd", "I"); + + field_mTimeoutRemainingMs = + get_field(env, clazz, "mTimeoutRemainingMs", "I"); +#endif +} + +static void initializeNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t)); + if (NULL == nat) { + LOGE("%s: out of memory!", __FUNCTION__); + return; + } + + nat->hcidev = BLUETOOTH_ADAPTER_HCI_NUM; + + env->SetIntField(object, field_mNativeData, (jint)nat); + nat->hf_ag_rfcomm_channel = + env->GetIntField(object, field_mHandsfreeAgRfcommChannel); + nat->hs_ag_rfcomm_channel = + env->GetIntField(object, field_mHeadsetAgRfcommChannel); + LOGV("HF RFCOMM channel = %d.", nat->hf_ag_rfcomm_channel); + LOGV("HS RFCOMM channel = %d.", nat->hs_ag_rfcomm_channel); + + /* Set the default values of these to -1. */ + env->SetIntField(object, field_mConnectingHeadsetRfcommChannel, -1); + env->SetIntField(object, field_mConnectingHandsfreeRfcommChannel, -1); + + nat->hf_ag_rfcomm_sock = -1; + nat->hs_ag_rfcomm_sock = -1; +#endif +} + +static void cleanupNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + free(nat); + } +#endif +} + +#ifdef HAVE_BLUETOOTH + +#if USE_ACCEPT_DIRECTLY==0 +static int set_nb(int sk, bool nb) { + int flags = fcntl(sk, F_GETFL); + if (flags < 0) { + LOGE("Can't get socket flags with fcntl(): %s (%d)", + strerror(errno), errno); + close(sk); + return -1; + } + flags &= ~O_NONBLOCK; + if (nb) flags |= O_NONBLOCK; + int status = fcntl(sk, F_SETFL, flags); + if (status < 0) { + LOGE("Can't set socket to nonblocking mode with fcntl(): %s (%d)", + strerror(errno), errno); + close(sk); + return -1; + } + return 0; +} +#endif /*USE_ACCEPT_DIRECTLY==0*/ + +static int do_accept(JNIEnv* env, jobject object, int ag_fd, + jfieldID out_fd, + jfieldID out_address, + jfieldID out_channel) { + +#if USE_ACCEPT_DIRECTLY==0 + if (set_nb(ag_fd, true) < 0) + return -1; +#endif + + struct sockaddr_rc raddr; + int alen = sizeof(raddr); + int nsk = accept(ag_fd, (struct sockaddr *) &raddr, &alen); + if (nsk < 0) { + LOGE("Error on accept from socket fd %d: %s (%d).", + ag_fd, + strerror(errno), + errno); +#if USE_ACCEPT_DIRECTLY==0 + set_nb(ag_fd, false); +#endif + return -1; + } + + env->SetIntField(object, out_fd, nsk); + env->SetIntField(object, out_channel, raddr.rc_channel); + + char addr[BTADDR_SIZE]; + get_bdaddr_as_string(&raddr.rc_bdaddr, addr); + env->SetObjectField(object, out_address, env->NewStringUTF(addr)); + + LOGI("Successful accept() on AG socket %d: new socket %d, address %s, RFCOMM channel %d", + ag_fd, + nsk, + addr, + raddr.rc_channel); +#if USE_ACCEPT_DIRECTLY==0 + set_nb(ag_fd, false); +#endif + return 0; +} + +#if USE_SELECT +static inline int on_accept_set_fields(JNIEnv* env, jobject object, + fd_set *rset, int ag_fd, + jfieldID out_fd, + jfieldID out_address, + jfieldID out_channel) { + + env->SetIntField(object, out_channel, -1); + + if (ag_fd >= 0 && FD_ISSET(ag_fd, &rset)) { + return do_accept(env, object, ag_fd, + out_fd, out_address, out_channel); + } + else { + LOGI("fd = %d, FD_ISSET() = %d", + ag_fd, + FD_ISSET(ag_fd, &rset)); + if (ag_fd >= 0 && !FD_ISSET(ag_fd, &rset)) { + LOGE("WTF???"); + return -1; + } + } + + return 0; +} +#endif +#endif /* HAVE_BLUETOOTH */ + +static jboolean waitForHandsfreeConnectNative(JNIEnv* env, jobject object, + jint timeout_ms) { +// LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + + env->SetIntField(object, field_mTimeoutRemainingMs, timeout_ms); + + int n = 0; + native_data_t *nat = get_native_data(env, object); +#if USE_ACCEPT_DIRECTLY + if (nat->hf_ag_rfcomm_channel > 0) { + LOGI("Setting HF AG server socket to RFCOMM port %d!", + nat->hf_ag_rfcomm_channel); + struct timeval tv; + int len = sizeof(tv); + if (getsockopt(nat->hf_ag_rfcomm_channel, + SOL_SOCKET, SO_RCVTIMEO, &tv, &len) < 0) { + LOGE("getsockopt(%d, SOL_SOCKET, SO_RCVTIMEO): %s (%d)", + nat->hf_ag_rfcomm_channel, + strerror(errno), + errno); + return JNI_FALSE; + } + LOGI("Current HF AG server socket RCVTIMEO is (%d(s), %d(us))!", + (int)tv.tv_sec, (int)tv.tv_usec); + if (timeout_ms >= 0) { + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = 1000 * (timeout_ms % 1000); + if (setsockopt(nat->hf_ag_rfcomm_channel, + SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + LOGE("setsockopt(%d, SOL_SOCKET, SO_RCVTIMEO): %s (%d)", + nat->hf_ag_rfcomm_channel, + strerror(errno), + errno); + return JNI_FALSE; + } + LOGI("Changed HF AG server socket RCVTIMEO to (%d(s), %d(us))!", + (int)tv.tv_sec, (int)tv.tv_usec); + } + + if (!do_accept(env, object, nat->hf_ag_rfcomm_sock, + field_mConnectingHandsfreeSocketFd, + field_mConnectingHandsfreeAddress, + field_mConnectingHandsfreeRfcommChannel)) + { + env->SetIntField(object, field_mTimeoutRemainingMs, 0); + return JNI_TRUE; + } + return JNI_FALSE; + } +#else +#if USE_SELECT + fd_set rset; + FD_ZERO(&rset); + int cnt = 0; + if (nat->hf_ag_rfcomm_channel > 0) { + LOGI("Setting HF AG server socket to RFCOMM port %d!", + nat->hf_ag_rfcomm_channel); + cnt++; + FD_SET(nat->hf_ag_rfcomm_sock, &rset); + } + if (nat->hs_ag_rfcomm_channel > 0) { + LOGI("Setting HS AG server socket to RFCOMM port %d!", + nat->hs_ag_rfcomm_channel); + cnt++; + FD_SET(nat->hs_ag_rfcomm_sock, &rset); + } + if (cnt == 0) { + LOGE("Neither HF nor HS listening sockets are open!"); + return JNI_FALSE; + } + + struct timeval to; + if (timeout_ms >= 0) { + to.tv_sec = timeout_ms / 1000; + to.tv_usec = 1000 * (timeout_ms % 1000); + } + n = select(MAX(nat->hf_ag_rfcomm_sock, + nat->hs_ag_rfcomm_sock) + 1, + &rset, + NULL, + NULL, + (timeout_ms < 0 ? NULL : &to)); + if (timeout_ms > 0) { + jint remaining = to.tv_sec*1000 + to.tv_usec/1000; + LOGI("Remaining time %ldms", (long)remaining); + env->SetIntField(object, field_mTimeoutRemainingMs, + remaining); + } + + LOGI("listening select() returned %d", n); + + if (n <= 0) { + if (n < 0) { + LOGE("listening select() on RFCOMM sockets: %s (%d)", + strerror(errno), + errno); + } + return JNI_FALSE; + } + + n = on_accept_set_fields(env, object, + &rset, nat->hf_ag_rfcomm_sock, + field_mConnectingHandsfreeSocketFd, + field_mConnectingHandsfreeAddress, + field_mConnectingHandsfreeRfcommChannel); + + n += on_accept_set_fields(env, object, + &rset, nat->hs_ag_rfcomm_sock, + field_mConnectingHeadsetSocketFd, + field_mConnectingHeadsetAddress, + field_mConnectingHeadsetRfcommChannel); + + return !n ? JNI_TRUE : JNI_FALSE; +#else + struct pollfd fds[2]; + int cnt = 0; + if (nat->hf_ag_rfcomm_channel > 0) { +// LOGI("Setting HF AG server socket %d to RFCOMM port %d!", +// nat->hf_ag_rfcomm_sock, +// nat->hf_ag_rfcomm_channel); + fds[cnt].fd = nat->hf_ag_rfcomm_sock; + fds[cnt].events = POLLIN | POLLPRI | POLLOUT | POLLERR; + cnt++; + } + if (nat->hs_ag_rfcomm_channel > 0) { +// LOGI("Setting HS AG server socket %d to RFCOMM port %d!", +// nat->hs_ag_rfcomm_sock, +// nat->hs_ag_rfcomm_channel); + fds[cnt].fd = nat->hs_ag_rfcomm_sock; + fds[cnt].events = POLLIN | POLLPRI | POLLOUT | POLLERR; + cnt++; + } + if (cnt == 0) { + LOGE("Neither HF nor HS listening sockets are open!"); + return JNI_FALSE; + } + n = poll(fds, cnt, timeout_ms); + if (n <= 0) { + if (n < 0) { + LOGE("listening poll() on RFCOMM sockets: %s (%d)", + strerror(errno), + errno); + } + else { + env->SetIntField(object, field_mTimeoutRemainingMs, 0); +// LOGI("listening poll() on RFCOMM socket timed out"); + } + return JNI_FALSE; + } + + //LOGI("listening poll() on RFCOMM socket returned %d", n); + int err = 0; + for (cnt = 0; cnt < (int)(sizeof(fds)/sizeof(fds[0])); cnt++) { + //LOGI("Poll on fd %d revent = %d.", fds[cnt].fd, fds[cnt].revents); + if (fds[cnt].fd == nat->hf_ag_rfcomm_sock) { + if (fds[cnt].revents & (POLLIN | POLLPRI | POLLOUT)) { + LOGI("Accepting HF connection.\n"); + err += do_accept(env, object, fds[cnt].fd, + field_mConnectingHandsfreeSocketFd, + field_mConnectingHandsfreeAddress, + field_mConnectingHandsfreeRfcommChannel); + n--; + } + } + else if (fds[cnt].fd == nat->hs_ag_rfcomm_sock) { + if (fds[cnt].revents & (POLLIN | POLLPRI | POLLOUT)) { + LOGI("Accepting HS connection.\n"); + err += do_accept(env, object, fds[cnt].fd, + field_mConnectingHeadsetSocketFd, + field_mConnectingHeadsetAddress, + field_mConnectingHeadsetRfcommChannel); + n--; + } + } + } /* for */ + + if (n != 0) { + LOGI("Bogus poll(): %d fake pollfd entrie(s)!", n); + return JNI_FALSE; + } + + return !err ? JNI_TRUE : JNI_FALSE; +#endif /* USE_SELECT */ +#endif /* USE_ACCEPT_DIRECTLY */ +#else + return JNI_FALSE; +#endif /* HAVE_BLUETOOTH */ +} + +static jboolean setUpListeningSocketsNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + + nat->hf_ag_rfcomm_sock = + setup_listening_socket(nat->hcidev, nat->hf_ag_rfcomm_channel); + if (nat->hf_ag_rfcomm_sock < 0) + return JNI_FALSE; + + nat->hs_ag_rfcomm_sock = + setup_listening_socket(nat->hcidev, nat->hs_ag_rfcomm_channel); + if (nat->hs_ag_rfcomm_sock < 0) { + close(nat->hf_ag_rfcomm_sock); + nat->hf_ag_rfcomm_sock = -1; + return JNI_FALSE; + } + + return JNI_TRUE; +#else + return JNI_FALSE; +#endif /* HAVE_BLUETOOTH */ +} + +#ifdef HAVE_BLUETOOTH +static int setup_listening_socket(int dev, int channel) { + struct sockaddr_rc laddr; + int sk, lm; + + sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sk < 0) { + LOGE("Can't create RFCOMM socket"); + return -1; + } + + if (debug_no_encrypt()) { + lm = RFCOMM_LM_AUTH; + } else { + lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; + } + + if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { + LOGE("Can't set RFCOMM link mode"); + close(sk); + return -1; + } + + laddr.rc_family = AF_BLUETOOTH; + bacpy(&laddr.rc_bdaddr, BDADDR_ANY); + laddr.rc_channel = channel; + + if (bind(sk, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) { + LOGE("Can't bind RFCOMM socket"); + close(sk); + return -1; + } + + listen(sk, 10); + return sk; +} +#endif /* HAVE_BLUETOOTH */ + +/* + private native void tearDownListeningSocketsNative(); +*/ +static void tearDownListeningSocketsNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + + if (nat->hf_ag_rfcomm_sock > 0) { + if (close(nat->hf_ag_rfcomm_sock) < 0) { + LOGE("Could not close HF server socket: %s (%d)\n", + strerror(errno), errno); + } + nat->hf_ag_rfcomm_sock = -1; + } + if (nat->hs_ag_rfcomm_sock > 0) { + if (close(nat->hs_ag_rfcomm_sock) < 0) { + LOGE("Could not close HS server socket: %s (%d)\n", + strerror(errno), errno); + } + nat->hs_ag_rfcomm_sock = -1; + } +#endif /* HAVE_BLUETOOTH */ +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative}, + {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, + + {"setUpListeningSocketsNative", "()Z", (void *)setUpListeningSocketsNative}, + {"tearDownListeningSocketsNative", "()V", (void *)tearDownListeningSocketsNative}, + {"waitForHandsfreeConnectNative", "(I)Z", (void *)waitForHandsfreeConnectNative}, +}; + +int register_android_bluetooth_BluetoothAudioGateway(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/bluetooth/BluetoothAudioGateway", sMethods, + NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/android_bluetooth_Database.cpp b/core/jni/android_bluetooth_Database.cpp new file mode 100644 index 0000000..136c9a3 --- /dev/null +++ b/core/jni/android_bluetooth_Database.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Database" +#define LOG_TAG "bluetooth_Database.cpp" + +#include "android_bluetooth_common.h" +#include "android_runtime/AndroidRuntime.h" +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" + +#ifdef HAVE_BLUETOOTH +#include <dbus/dbus.h> +#endif + +namespace android { + +#ifdef HAVE_BLUETOOTH +static DBusConnection* conn = NULL; // Singleton thread-safe connection +#endif + +static void classInitNative(JNIEnv* env, jclass clazz) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + conn = NULL; +#endif +} + +static void initializeNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); + +#ifdef HAVE_BLUETOOTH + if (conn == NULL) { + DBusError err; + dbus_error_init(&err); + dbus_threads_init_default(); + conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (dbus_error_is_set(&err)) { + LOGE("Could not get onto the system bus!"); + dbus_error_free(&err); + } + } +#endif +} + +static void cleanupNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +} + +static jint addServiceRecordNative(JNIEnv *env, jobject object, + jbyteArray record) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + if (conn != NULL) { + jbyte* c_record = env->GetByteArrayElements(record, NULL); + DBusMessage *reply = dbus_func_args(env, + conn, + BLUEZ_DBUS_BASE_PATH, + DBUS_CLASS_NAME, + "AddServiceRecord", + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &c_record, + env->GetArrayLength(record), + DBUS_TYPE_INVALID); + env->ReleaseByteArrayElements(record, c_record, JNI_ABORT); + return reply ? dbus_returns_uint32(env, reply) : -1; + } +#endif + return -1; +} + +static jint addServiceRecordFromXmlNative(JNIEnv *env, jobject object, + jstring record) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + if (conn != NULL) { + const char *c_record = env->GetStringUTFChars(record, NULL); + DBusMessage *reply = dbus_func_args(env, + conn, + BLUEZ_DBUS_BASE_PATH, + DBUS_CLASS_NAME, + "AddServiceRecordFromXML", + DBUS_TYPE_STRING, &c_record, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(record, c_record); + return reply ? dbus_returns_uint32(env, reply) : -1; + } +#endif + return -1; +} + +static void updateServiceRecordNative(JNIEnv *env, jobject object, + jint handle, + jbyteArray record) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + if (conn != NULL) { + jbyte* c_record = env->GetByteArrayElements(record, NULL); + DBusMessage *reply = dbus_func_args(env, + conn, + BLUEZ_DBUS_BASE_PATH, + DBUS_CLASS_NAME, + "UpdateServiceRecord", + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &c_record, + env->GetArrayLength(record), + DBUS_TYPE_INVALID); + env->ReleaseByteArrayElements(record, c_record, JNI_ABORT); + } +#endif +} + +static void updateServiceRecordFromXmlNative(JNIEnv *env, jobject object, + jint handle, + jstring record) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + if (conn != NULL) { + const char *c_record = env->GetStringUTFChars(record, NULL); + DBusMessage *reply = dbus_func_args(env, + conn, + BLUEZ_DBUS_BASE_PATH, + DBUS_CLASS_NAME, + "UpdateServiceRecordFromXML", + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_STRING, &c_record, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(record, c_record); + } +#endif +} + +/* private static native void removeServiceRecordNative(int handle); */ +static void removeServiceRecordNative(JNIEnv *env, jobject object, + jint handle) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + if (conn != NULL) { + DBusMessage *reply = dbus_func_args(env, + conn, + BLUEZ_DBUS_BASE_PATH, + DBUS_CLASS_NAME, + "RemoveServiceRecord", + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID); + } +#endif +} + + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative}, + {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, + {"addServiceRecordNative", "([B)I", (void*)addServiceRecordNative}, + {"addServiceRecordFromXmlNative", "(Ljava/lang/String;)I", (void*)addServiceRecordFromXmlNative}, + {"updateServiceRecordNative", "(I[B)V", (void*)updateServiceRecordNative}, + {"updateServiceRecordFromXmlNative", "(ILjava/lang/String;)V", (void*)updateServiceRecordFromXmlNative}, + {"removeServiceRecordNative", "(I)V", (void*)removeServiceRecordNative}, +}; + +int register_android_bluetooth_Database(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/bluetooth/Database", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/android_bluetooth_HeadsetBase.cpp b/core/jni/android_bluetooth_HeadsetBase.cpp new file mode 100644 index 0000000..bb19e92 --- /dev/null +++ b/core/jni/android_bluetooth_HeadsetBase.cpp @@ -0,0 +1,548 @@ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "BT HSHFP" + +#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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/poll.h> + +#ifdef HAVE_BLUETOOTH +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> +#include <bluetooth/sco.h> +#endif + +namespace android { + +#ifdef HAVE_BLUETOOTH +static jfieldID field_mNativeData; +static jfieldID field_mAddress; +static jfieldID field_mRfcommChannel; +static jfieldID field_mTimeoutRemainingMs; + +typedef struct { + jstring address; + const char *c_address; + int rfcomm_channel; + int last_read_err; + int rfcomm_sock; + int rfcomm_connected; // -1 in progress, 0 not connected, 1 connected + int rfcomm_sock_flags; +} native_data_t; + +static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { + return (native_data_t *)(env->GetIntField(object, field_mNativeData)); +} + +static const char CRLF[] = "\xd\xa"; +static const int CRLF_LEN = 2; + +static inline int write_error_check(int fd, const char* line, int len) { + int ret; + errno = 0; + ret = write(fd, line, len); + if (ret < 0) { + LOGE("%s: write() failed: %s (%d)", __FUNCTION__, strerror(errno), + errno); + return -1; + } + if (ret != len) { + LOGE("%s: write() only wrote %d of %d bytes", __FUNCTION__, ret, len); + return -1; + } + return 0; +} + +static int send_line(int fd, const char* line) { + int nw; + int len = strlen(line); + int llen = len + CRLF_LEN * 2 + 1; + char *buffer = (char *)calloc(llen, sizeof(char)); + + snprintf(buffer, llen, "%s%s%s", CRLF, line, CRLF); + + if (write_error_check(fd, buffer, llen - 1)) { + free(buffer); + return -1; + } + free(buffer); + return 0; +} + +static const char* get_line(int fd, char *buf, int len, int timeout_ms, + int *err) { + char *bufit=buf; + int fd_flags = fcntl(fd, F_GETFL, 0); + struct pollfd pfd; + +again: + *bufit = 0; + pfd.fd = fd; + pfd.events = POLLIN; + *err = errno = 0; + int ret = poll(&pfd, 1, timeout_ms); + if (ret < 0) { + LOGE("poll() error\n"); + *err = errno; + return NULL; + } + if (ret == 0) { + return NULL; + } + + if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) { + LOGW("RFCOMM poll() returned success (%d), " + "but with an unexpected revents bitmask: %#x\n", ret, pfd.revents); + errno = EIO; + *err = errno; + return NULL; + } + + while ((int)(bufit - buf) < len) + { + errno = 0; + int rc = read(fd, bufit, 1); + + if (!rc) + break; + + if (rc < 0) { + if (errno == EBUSY) { + LOGI("read() error %s (%d): repeating read()...", + strerror(errno), errno); + goto again; + } + *err = errno; + LOGE("read() error %s (%d)", strerror(errno), errno); + return NULL; + } + + + if (*bufit=='\xd') { + break; + } + + if (*bufit=='\xa') + bufit = buf; + else + bufit++; + } + + *bufit = '\x0'; + LOG(LOG_INFO, "Bluetooth AT recv", buf); + + return buf; +} +#endif + +static void classInitNative(JNIEnv* env, jclass clazz) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + field_mNativeData = get_field(env, clazz, "mNativeData", "I"); + field_mAddress = get_field(env, clazz, "mAddress", "Ljava/lang/String;"); + field_mTimeoutRemainingMs = get_field(env, clazz, "mTimeoutRemainingMs", "I"); + field_mRfcommChannel = get_field(env, clazz, "mRfcommChannel", "I"); +#endif +} + +static void initializeNativeDataNative(JNIEnv* env, jobject object, + jint socketFd) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t)); + if (NULL == nat) { + LOGE("%s: out of memory!", __FUNCTION__); + return; + } + + env->SetIntField(object, field_mNativeData, (jint)nat); + nat->address = + (jstring)env->NewGlobalRef(env->GetObjectField(object, + field_mAddress)); + nat->c_address = env->GetStringUTFChars(nat->address, NULL); + nat->rfcomm_channel = env->GetIntField(object, field_mRfcommChannel); + nat->rfcomm_sock = socketFd; + nat->rfcomm_connected = socketFd >= 0; + if (nat->rfcomm_connected) + LOGI("%s: ALREADY CONNECTED!", __FUNCTION__); +#endif +} + +static void cleanupNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = + (native_data_t *)env->GetIntField(object, field_mNativeData); + env->ReleaseStringUTFChars(nat->address, nat->c_address); + env->DeleteGlobalRef(nat->address); + if (nat) + free(nat); +#endif +} + +static jboolean connectNative(JNIEnv *env, jobject obj) +{ + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + int lm; + struct sockaddr_rc addr; + native_data_t *nat = get_native_data(env, obj); + + nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + + if (nat->rfcomm_sock < 0) { + LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__, + strerror(errno)); + return JNI_FALSE; + } + + if (debug_no_encrypt()) { + lm = RFCOMM_LM_AUTH; + } else { + lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; + } + + if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm, + sizeof(lm)) < 0) { + LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__); + close(nat->rfcomm_sock); + return JNI_FALSE; + } + + memset(&addr, 0, sizeof(struct sockaddr_rc)); + get_bdaddr(nat->c_address, &addr.rc_bdaddr); + addr.rc_channel = nat->rfcomm_channel; + addr.rc_family = AF_BLUETOOTH; + nat->rfcomm_connected = 0; + while (nat->rfcomm_connected == 0) { + if (connect(nat->rfcomm_sock, (struct sockaddr *)&addr, + sizeof(addr)) < 0) { + if (errno == EINTR) continue; + LOGE("%s: connect() failed: %s\n", __FUNCTION__, strerror(errno)); + close(nat->rfcomm_sock); + nat->rfcomm_sock = -1; + return JNI_FALSE; + } else { + nat->rfcomm_connected = 1; + } + } + + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +static jboolean connectAsyncNative(JNIEnv *env, jobject obj) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + struct sockaddr_rc addr; + native_data_t *nat = get_native_data(env, obj); + + if (nat->rfcomm_connected) { + LOGV("RFCOMM socket is already connected or connection is in progress."); + return JNI_TRUE; + } + + if (nat->rfcomm_sock < 0) { + int lm; + + nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (nat->rfcomm_sock < 0) { + LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__, + strerror(errno)); + return JNI_FALSE; + } + + if (debug_no_encrypt()) { + lm = RFCOMM_LM_AUTH; + } else { + lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; + } + + if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm, + sizeof(lm)) < 0) { + LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__); + close(nat->rfcomm_sock); + return JNI_FALSE; + } + LOGI("Created RFCOMM socket fd %d.", nat->rfcomm_sock); + } + + memset(&addr, 0, sizeof(struct sockaddr_rc)); + get_bdaddr(nat->c_address, &addr.rc_bdaddr); + addr.rc_channel = nat->rfcomm_channel; + addr.rc_family = AF_BLUETOOTH; + if (nat->rfcomm_sock_flags >= 0) { + nat->rfcomm_sock_flags = fcntl(nat->rfcomm_sock, F_GETFL, 0); + if (fcntl(nat->rfcomm_sock, + F_SETFL, nat->rfcomm_sock_flags | O_NONBLOCK) >= 0) { + int rc; + nat->rfcomm_connected = 0; + errno = 0; + rc = connect(nat->rfcomm_sock, + (struct sockaddr *)&addr, + sizeof(addr)); + + if (rc >= 0) { + nat->rfcomm_connected = 1; + LOGI("async connect successful"); + return JNI_TRUE; + } + else if (rc < 0) { + if (errno == EINPROGRESS || errno == EAGAIN) + { + LOGI("async connect is in progress (%s)", + strerror(errno)); + nat->rfcomm_connected = -1; + return JNI_TRUE; + } + else + { + LOGE("async connect error: %s (%d)", strerror(errno), errno); + close(nat->rfcomm_sock); + nat->rfcomm_sock = -1; + return JNI_FALSE; + } + } + } // fcntl(nat->rfcomm_sock ...) + } // if (nat->rfcomm_sock_flags >= 0) +#endif + return JNI_FALSE; +} + +static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj, + jint timeout_ms) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + struct sockaddr_rc addr; + native_data_t *nat = get_native_data(env, obj); + + env->SetIntField(obj, field_mTimeoutRemainingMs, timeout_ms); + + if (nat->rfcomm_connected > 0) { + LOGI("RFCOMM is already connected!"); + return 1; + } + + if (nat->rfcomm_sock >= 0 && nat->rfcomm_connected == 0) { + LOGI("Re-opening RFCOMM socket."); + close(nat->rfcomm_sock); + nat->rfcomm_sock = -1; + } + if (JNI_FALSE == connectAsyncNative(env, obj)) { + LOGI("Failed to re-open RFCOMM socket!"); + return -1; + } + + if (nat->rfcomm_sock >= 0) { + /* Do an asynchronous select() */ + int n; + fd_set rset, wset; + struct timeval to; + + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_SET(nat->rfcomm_sock, &rset); + FD_SET(nat->rfcomm_sock, &wset); + if (timeout_ms >= 0) { + to.tv_sec = timeout_ms / 1000; + to.tv_usec = 1000 * (timeout_ms % 1000); + } + n = select(nat->rfcomm_sock + 1, + &rset, + &wset, + NULL, + (timeout_ms < 0 ? NULL : &to)); + + if (timeout_ms > 0) { + jint remaining = to.tv_sec*1000 + to.tv_usec/1000; + LOGV("Remaining time %ldms", (long)remaining); + env->SetIntField(obj, field_mTimeoutRemainingMs, + remaining); + } + + if (n <= 0) { + if (n < 0) { + LOGE("select() on RFCOMM socket: %s (%d)", + strerror(errno), + errno); + return -1; + } + return 0; + } + /* n must be equal to 1 and either rset or wset must have the + file descriptor set. */ + LOGV("select() returned %d.", n); + if (FD_ISSET(nat->rfcomm_sock, &rset) || + FD_ISSET(nat->rfcomm_sock, &wset)) + { + /* A trial async read() will tell us if everything is OK. */ + { + char ch; + errno = 0; + int nr = read(nat->rfcomm_sock, &ch, 1); + /* It should be that nr != 1 because we just opened a socket + and we haven't sent anything over it for the other side to + respond... but one can't be paranoid enough. + */ + if (nr >= 0 || errno != EAGAIN) { + LOGE("RFCOMM async connect() error: %s (%d), nr = %d\n", + strerror(errno), + errno, + nr); + /* Clear the rfcomm_connected flag to cause this function + to re-create the socket and re-attempt the connect() + the next time it is called. + */ + nat->rfcomm_connected = 0; + /* Restore the blocking properties of the socket. */ + fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags); + close(nat->rfcomm_sock); + nat->rfcomm_sock = -1; + return -1; + } + } + /* Restore the blocking properties of the socket. */ + fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags); + LOGI("Successful RFCOMM socket connect."); + nat->rfcomm_connected = 1; + return 1; + } + } + else LOGE("RFCOMM socket file descriptor %d is bad!", + nat->rfcomm_sock); +#endif + return -1; +} + +static void disconnectNative(JNIEnv *env, jobject obj) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, obj); + if (nat->rfcomm_sock >= 0) { + close(nat->rfcomm_sock); + nat->rfcomm_sock = -1; + nat->rfcomm_connected = 0; + } +#endif +} + +static void pretty_log_urc(const char *urc) { + size_t i; + bool in_line_break = false; + char *buf = (char *)calloc(strlen(urc) + 1, sizeof(char)); + + strcpy(buf, urc); + for (i = 0; i < strlen(buf); i++) { + switch(buf[i]) { + case '\r': + case '\n': + in_line_break = true; + buf[i] = ' '; + break; + default: + if (in_line_break) { + in_line_break = false; + buf[i-1] = '\n'; + } + } + } + LOG(LOG_INFO, "Bluetooth AT sent", buf); + + free(buf); +} + +static jboolean sendURCNative(JNIEnv *env, jobject obj, jstring urc) { +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, obj); + if (nat->rfcomm_connected) { + const char *c_urc = env->GetStringUTFChars(urc, NULL); + jboolean ret = send_line(nat->rfcomm_sock, c_urc) == 0 ? JNI_TRUE : JNI_FALSE; + if (ret == JNI_TRUE) pretty_log_urc(c_urc); + env->ReleaseStringUTFChars(urc, c_urc); + return ret; + } +#endif + return JNI_FALSE; +} + +static jstring readNative(JNIEnv *env, jobject obj, jint timeout_ms) { +#ifdef HAVE_BLUETOOTH + { + native_data_t *nat = get_native_data(env, obj); + if (nat->rfcomm_connected) { + char buf[128]; + const char *ret = get_line(nat->rfcomm_sock, + buf, sizeof(buf), + timeout_ms, + &nat->last_read_err); + return ret ? env->NewStringUTF(ret) : NULL; + } + return NULL; + } +#else + return NULL; +#endif +} + +static jint getLastReadStatusNative(JNIEnv *env, jobject obj) { +#ifdef HAVE_BLUETOOTH + { + native_data_t *nat = get_native_data(env, obj); + if (nat->rfcomm_connected) + return (jint)nat->last_read_err; + return 0; + } +#else + return 0; +#endif +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNativeDataNative", "(I)V", (void *)initializeNativeDataNative}, + {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, + {"connectNative", "()Z", (void *)connectNative}, + {"connectAsyncNative", "()Z", (void *)connectAsyncNative}, + {"waitForAsyncConnectNative", "(I)I", (void *)waitForAsyncConnectNative}, + {"disconnectNative", "()V", (void *)disconnectNative}, + {"sendURCNative", "(Ljava/lang/String;)Z", (void *)sendURCNative}, + {"readNative", "(I)Ljava/lang/String;", (void *)readNative}, + {"getLastReadStatusNative", "()I", (void *)getLastReadStatusNative}, +}; + +int register_android_bluetooth_HeadsetBase(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/bluetooth/HeadsetBase", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/android_bluetooth_RfcommSocket.cpp b/core/jni/android_bluetooth_RfcommSocket.cpp new file mode 100644 index 0000000..3ed35d9 --- /dev/null +++ b/core/jni/android_bluetooth_RfcommSocket.cpp @@ -0,0 +1,621 @@ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "bluetooth_RfcommSocket.cpp" + +#include "android_bluetooth_common.h" +#include "android_runtime/AndroidRuntime.h" +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/poll.h> + +#ifdef HAVE_BLUETOOTH +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> +#include <bluetooth/sco.h> +#endif + +namespace android { + +#ifdef HAVE_BLUETOOTH +static jfieldID field_mNativeData; +static jfieldID field_mTimeoutRemainingMs; +static jfieldID field_mAcceptTimeoutRemainingMs; +static jfieldID field_mAddress; +static jfieldID field_mPort; + +typedef struct { + jstring address; + const char *c_address; + int rfcomm_channel; + int last_read_err; + int rfcomm_sock; + // < 0 -- in progress, + // 0 -- not connected + // > 0 connected + // 1 input is open + // 2 output is open + // 3 both input and output are open + int rfcomm_connected; + int rfcomm_sock_flags; +} native_data_t; + +static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { + return (native_data_t *)(env->GetIntField(object, field_mNativeData)); +} + +static inline void init_socket_info( + JNIEnv *env, jobject object, + native_data_t *nat, + jstring address, + jint rfcomm_channel) { + nat->address = (jstring)env->NewGlobalRef(address); + nat->c_address = env->GetStringUTFChars(nat->address, NULL); + nat->rfcomm_channel = (int)rfcomm_channel; +} + +static inline void cleanup_socket_info(JNIEnv *env, native_data_t *nat) { + if (nat->c_address != NULL) { + env->ReleaseStringUTFChars(nat->address, nat->c_address); + env->DeleteGlobalRef(nat->address); + nat->c_address = NULL; + } +} +#endif + +static void classInitNative(JNIEnv* env, jclass clazz) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + field_mNativeData = get_field(env, clazz, "mNativeData", "I"); + field_mTimeoutRemainingMs = get_field(env, clazz, "mTimeoutRemainingMs", "I"); + field_mAcceptTimeoutRemainingMs = get_field(env, clazz, "mAcceptTimeoutRemainingMs", "I"); + field_mAddress = get_field(env, clazz, "mAddress", "Ljava/lang/String;"); + field_mPort = get_field(env, clazz, "mPort", "I"); +#endif +} + +static void initializeNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + + native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t)); + if (nat == NULL) { + LOGE("%s: out of memory!", __FUNCTION__); + return; + } + + env->SetIntField(object, field_mNativeData, (jint)nat); + nat->rfcomm_sock = -1; + nat->rfcomm_connected = 0; +#endif +} + +static void cleanupNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + free(nat); + } +#endif +} + +static jobject createNative(JNIEnv *env, jobject obj) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + int lm; + native_data_t *nat = get_native_data(env, obj); + nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + + if (nat->rfcomm_sock < 0) { + LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__, + strerror(errno)); + return NULL; + } + + lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; + + if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm, + sizeof(lm)) < 0) { + LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__); + close(nat->rfcomm_sock); + return NULL; + } + + return jniCreateFileDescriptor(env, nat->rfcomm_sock); +#else + return NULL; +#endif +} + +static void destroyNative(JNIEnv *env, jobject obj) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, obj); + cleanup_socket_info(env, nat); + if (nat->rfcomm_sock >= 0) { + close(nat->rfcomm_sock); + nat->rfcomm_sock = -1; + } +#endif +} + + +static jboolean connectNative(JNIEnv *env, jobject obj, + jstring address, jint port) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, obj); + + if (nat->rfcomm_sock >= 0) { + if (nat->rfcomm_connected) { + LOGI("RFCOMM socket: %s.", + (nat->rfcomm_connected > 0) ? "already connected" : "connection is in progress"); + return JNI_TRUE; + } + + init_socket_info(env, obj, nat, address, port); + + struct sockaddr_rc addr; + memset(&addr, 0, sizeof(struct sockaddr_rc)); + get_bdaddr(nat->c_address, &addr.rc_bdaddr); + addr.rc_channel = nat->rfcomm_channel; + addr.rc_family = AF_BLUETOOTH; + nat->rfcomm_connected = 0; + + while (nat->rfcomm_connected == 0) { + if (connect(nat->rfcomm_sock, (struct sockaddr *)&addr, + sizeof(addr)) < 0) { + if (errno == EINTR) continue; + LOGE("connect error: %s (%d)\n", strerror(errno), errno); + break; + } else { + nat->rfcomm_connected = 3; // input and output + } + } + } else { + LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__); + } + + if (nat->rfcomm_connected > 0) { + env->SetIntField(obj, field_mPort, port); + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jboolean connectAsyncNative(JNIEnv *env, jobject obj, + jstring address, jint port) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, obj); + + if (nat->rfcomm_sock < 0) { + LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__); + return JNI_FALSE; + } + + if (nat->rfcomm_connected) { + LOGI("RFCOMM socket: %s.", + (nat->rfcomm_connected > 0) ? + "already connected" : "connection is in progress"); + return JNI_TRUE; + } + + init_socket_info(env, obj, nat, address, port); + + struct sockaddr_rc addr; + memset(&addr, 0, sizeof(struct sockaddr_rc)); + get_bdaddr(nat->c_address, &addr.rc_bdaddr); + addr.rc_channel = nat->rfcomm_channel; + addr.rc_family = AF_BLUETOOTH; + + nat->rfcomm_sock_flags = fcntl(nat->rfcomm_sock, F_GETFL, 0); + if (fcntl(nat->rfcomm_sock, + F_SETFL, nat->rfcomm_sock_flags | O_NONBLOCK) >= 0) { + int rc; + nat->rfcomm_connected = 0; + errno = 0; + rc = connect(nat->rfcomm_sock, + (struct sockaddr *)&addr, + sizeof(addr)); + + if (rc >= 0) { + nat->rfcomm_connected = 3; + LOGI("RFCOMM async connect immediately successful"); + env->SetIntField(obj, field_mPort, port); + return JNI_TRUE; + } + else if (rc < 0) { + if (errno == EINPROGRESS || errno == EAGAIN) + { + LOGI("RFCOMM async connect is in progress (%s)", + strerror(errno)); + nat->rfcomm_connected = -1; + env->SetIntField(obj, field_mPort, port); + return JNI_TRUE; + } + else + { + LOGE("RFCOMM async connect error (%d): %s (%d)", + nat->rfcomm_sock, strerror(errno), errno); + return JNI_FALSE; + } + } + } // fcntl(nat->rfcomm_sock ...) +#endif + return JNI_FALSE; +} + +static jboolean interruptAsyncConnectNative(JNIEnv *env, jobject obj) { + //WRITEME + return JNI_TRUE; +} + +static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj, + jint timeout_ms) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + struct sockaddr_rc addr; + native_data_t *nat = get_native_data(env, obj); + + env->SetIntField(obj, field_mTimeoutRemainingMs, timeout_ms); + + if (nat->rfcomm_sock < 0) { + LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__); + return -1; + } + + if (nat->rfcomm_connected > 0) { + LOGI("%s: RFCOMM is already connected!", __FUNCTION__); + return 1; + } + + /* Do an asynchronous select() */ + int n; + fd_set rset, wset; + struct timeval to; + + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_SET(nat->rfcomm_sock, &rset); + FD_SET(nat->rfcomm_sock, &wset); + if (timeout_ms >= 0) { + to.tv_sec = timeout_ms / 1000; + to.tv_usec = 1000 * (timeout_ms % 1000); + } + n = select(nat->rfcomm_sock + 1, + &rset, + &wset, + NULL, + (timeout_ms < 0 ? NULL : &to)); + + if (timeout_ms > 0) { + jint remaining = to.tv_sec*1000 + to.tv_usec/1000; + LOGI("Remaining time %ldms", (long)remaining); + env->SetIntField(obj, field_mTimeoutRemainingMs, + remaining); + } + + if (n <= 0) { + if (n < 0) { + LOGE("select() on RFCOMM socket: %s (%d)", + strerror(errno), + errno); + return -1; + } + return 0; + } + /* n must be equal to 1 and either rset or wset must have the + file descriptor set. */ + LOGI("select() returned %d.", n); + if (FD_ISSET(nat->rfcomm_sock, &rset) || + FD_ISSET(nat->rfcomm_sock, &wset)) { + /* A trial async read() will tell us if everything is OK. */ + char ch; + errno = 0; + int nr = read(nat->rfcomm_sock, &ch, 1); + /* It should be that nr != 1 because we just opened a socket + and we haven't sent anything over it for the other side to + respond... but one can't be paranoid enough. + */ + if (nr >= 0 || errno != EAGAIN) { + LOGE("RFCOMM async connect() error: %s (%d), nr = %d\n", + strerror(errno), + errno, + nr); + /* Clear the rfcomm_connected flag to cause this function + to re-create the socket and re-attempt the connect() + the next time it is called. + */ + nat->rfcomm_connected = 0; + /* Restore the blocking properties of the socket. */ + fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags); + return -1; + } + /* Restore the blocking properties of the socket. */ + fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags); + LOGI("Successful RFCOMM socket connect."); + nat->rfcomm_connected = 3; // input and output + return 1; + } +#endif + return -1; +} + +static jboolean shutdownNative(JNIEnv *env, jobject obj, + jboolean shutdownInput) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + /* NOTE: If you change the bcode to modify nat, make sure you + add synchronize(this) to the method calling this native + method. + */ + native_data_t *nat = get_native_data(env, obj); + if (nat->rfcomm_sock < 0) { + LOGE("socket(RFCOMM) error: socket not created"); + return JNI_FALSE; + } + int rc = shutdown(nat->rfcomm_sock, + shutdownInput ? SHUT_RD : SHUT_WR); + if (!rc) { + nat->rfcomm_connected &= + shutdownInput ? ~1 : ~2; + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jint isConnectedNative(JNIEnv *env, jobject obj) { + LOGI(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + const native_data_t *nat = get_native_data(env, obj); + return nat->rfcomm_connected; +#endif + return 0; +} + +//@@@@@@@@@ bind to device??? +static jboolean bindNative(JNIEnv *env, jobject obj, jstring device, + jint port) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + + /* NOTE: If you change the code to modify nat, make sure you + add synchronize(this) to the method calling this native + method. + */ + const native_data_t *nat = get_native_data(env, obj); + if (nat->rfcomm_sock < 0) { + LOGE("socket(RFCOMM) error: socket not created"); + return JNI_FALSE; + } + + struct sockaddr_rc laddr; + int lm; + + lm = 0; +/* + lm |= RFCOMM_LM_MASTER; + lm |= RFCOMM_LM_AUTH; + lm |= RFCOMM_LM_ENCRYPT; + lm |= RFCOMM_LM_SECURE; +*/ + + if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { + LOGE("Can't set RFCOMM link mode"); + return JNI_FALSE; + } + + laddr.rc_family = AF_BLUETOOTH; + bacpy(&laddr.rc_bdaddr, BDADDR_ANY); + laddr.rc_channel = port; + + if (bind(nat->rfcomm_sock, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) { + LOGE("Can't bind RFCOMM socket"); + return JNI_FALSE; + } + + env->SetIntField(obj, field_mPort, port); + + return JNI_TRUE; +#endif + return JNI_FALSE; +} + +static jboolean listenNative(JNIEnv *env, jobject obj, jint backlog) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + /* NOTE: If you change the code to modify nat, make sure you + add synchronize(this) to the method calling this native + method. + */ + const native_data_t *nat = get_native_data(env, obj); + if (nat->rfcomm_sock < 0) { + LOGE("socket(RFCOMM) error: socket not created"); + return JNI_FALSE; + } + return listen(nat->rfcomm_sock, backlog) < 0 ? JNI_FALSE : JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +static int set_nb(int sk, bool nb) { + int flags = fcntl(sk, F_GETFL); + if (flags < 0) { + LOGE("Can't get socket flags with fcntl(): %s (%d)", + strerror(errno), errno); + close(sk); + return -1; + } + flags &= ~O_NONBLOCK; + if (nb) flags |= O_NONBLOCK; + int status = fcntl(sk, F_SETFL, flags); + if (status < 0) { + LOGE("Can't set socket to nonblocking mode with fcntl(): %s (%d)", + strerror(errno), errno); + close(sk); + return -1; + } + return 0; +} + +// Note: the code should check at a higher level to see whether +// listen() has been called. +#ifdef HAVE_BLUETOOTH +static int do_accept(JNIEnv* env, jobject object, int sock, + jobject newsock, + jfieldID out_address, + bool must_succeed) { + + if (must_succeed && set_nb(sock, true) < 0) + return -1; + + struct sockaddr_rc raddr; + int alen = sizeof(raddr); + int nsk = accept(sock, (struct sockaddr *) &raddr, &alen); + if (nsk < 0) { + LOGE("Error on accept from socket fd %d: %s (%d).", + sock, + strerror(errno), + errno); + if (must_succeed) set_nb(sock, false); + return -1; + } + + char addr[BTADDR_SIZE]; + get_bdaddr_as_string(&raddr.rc_bdaddr, addr); + env->SetObjectField(newsock, out_address, env->NewStringUTF(addr)); + + LOGI("Successful accept() on AG socket %d: new socket %d, address %s, RFCOMM channel %d", + sock, + nsk, + addr, + raddr.rc_channel); + if (must_succeed) set_nb(sock, false); + return nsk; +} +#endif /*HAVE_BLUETOOTH*/ + +static jobject acceptNative(JNIEnv *env, jobject obj, + jobject newsock, jint timeoutMs) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, obj); + if (nat->rfcomm_sock < 0) { + LOGE("socket(RFCOMM) error: socket not created"); + return JNI_FALSE; + } + + if (newsock == NULL) { + LOGE("%s: newsock = NULL\n", __FUNCTION__); + return JNI_FALSE; + } + + int nsk = -1; + if (timeoutMs < 0) { + /* block until accept() succeeds */ + nsk = do_accept(env, obj, nat->rfcomm_sock, + newsock, field_mAddress, false); + if (nsk < 0) { + return NULL; + } + } + else { + /* wait with a timeout */ + + struct pollfd fds; + fds.fd = nat->rfcomm_sock; + fds.events = POLLIN | POLLPRI | POLLOUT | POLLERR; + + env->SetIntField(obj, field_mAcceptTimeoutRemainingMs, 0); + int n = poll(&fds, 1, timeoutMs); + if (n <= 0) { + if (n < 0) { + LOGE("listening poll() on RFCOMM socket: %s (%d)", + strerror(errno), + errno); + env->SetIntField(obj, field_mAcceptTimeoutRemainingMs, timeoutMs); + } + else { + LOGI("listening poll() on RFCOMM socket timed out"); + } + return NULL; + } + + LOGI("listening poll() on RFCOMM socket returned %d", n); + if (fds.fd == nat->rfcomm_sock) { + if (fds.revents & (POLLIN | POLLPRI | POLLOUT)) { + LOGI("Accepting connection.\n"); + nsk = do_accept(env, obj, nat->rfcomm_sock, + newsock, field_mAddress, true); + if (nsk < 0) { + return NULL; + } + } + } + } + + LOGI("Connection accepted, new socket fd = %d.", nsk); + native_data_t *newnat = get_native_data(env, newsock); + newnat->rfcomm_sock = nsk; + newnat->rfcomm_connected = 3; + return jniCreateFileDescriptor(env, nsk); +#else + return NULL; +#endif +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative}, + {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, + + {"createNative", "()Ljava/io/FileDescriptor;", (void *)createNative}, + {"destroyNative", "()V", (void *)destroyNative}, + {"connectNative", "(Ljava/lang/String;I)Z", (void *)connectNative}, + {"connectAsyncNative", "(Ljava/lang/String;I)Z", (void *)connectAsyncNative}, + {"interruptAsyncConnectNative", "()Z", (void *)interruptAsyncConnectNative}, + {"waitForAsyncConnectNative", "(I)I", (void *)waitForAsyncConnectNative}, + {"shutdownNative", "(Z)Z", (void *)shutdownNative}, + {"isConnectedNative", "()I", (void *)isConnectedNative}, + + {"bindNative", "(Ljava/lang/String;I)Z", (void*)bindNative}, + {"listenNative", "(I)Z", (void*)listenNative}, + {"acceptNative", "(Landroid/bluetooth/RfcommSocket;I)Ljava/io/FileDescriptor;", (void*)acceptNative}, +}; + +int register_android_bluetooth_RfcommSocket(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/bluetooth/RfcommSocket", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/android_bluetooth_ScoSocket.cpp b/core/jni/android_bluetooth_ScoSocket.cpp new file mode 100644 index 0000000..3afe5f5 --- /dev/null +++ b/core/jni/android_bluetooth_ScoSocket.cpp @@ -0,0 +1,506 @@ +/* +** 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 "bluetooth_ScoSocket.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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/poll.h> + +#ifdef HAVE_BLUETOOTH +#include <bluetooth/bluetooth.h> +#include <bluetooth/sco.h> +#endif + +/* Ideally, blocking I/O on a SCO socket would return when another thread + * calls close(). However it does not right now, in fact close() on a SCO + * socket has strange behavior (returns a bogus value) when other threads + * are performing blocking I/O on that socket. So, to workaround, we always + * call close() from the same thread that does blocking I/O. This requires the + * use of a socketpair to signal the blocking I/O to abort. + * + * Unfortunately I don't know a way to abort connect() yet, but at least this + * times out after the BT page timeout (10 seconds currently), so the thread + * will die eventually. The fact that the thread can outlive + * the Java object forces us to use a mutex in destoryNative(). + * + * The JNI API is entirely async. + * + * Also note this class deals only with SCO connections, not with data + * transmission. + */ +namespace android { +#ifdef HAVE_BLUETOOTH + +static JavaVM *jvm; +static jfieldID field_mNativeData; +static jmethodID method_onAccepted; +static jmethodID method_onConnected; +static jmethodID method_onClosed; + +struct thread_data_t; +static void *work_thread(void *arg); +static int connect_work(const char *address); +static int accept_work(int signal_sk); +static void wait_for_close(int sk, int signal_sk); +static void closeNative(JNIEnv *env, jobject object); + +/* shared native data - protected by mutex */ +typedef struct { + pthread_mutex_t mutex; + int signal_sk; // socket to signal blocked I/O to unblock + jobject object; // JNI global ref to the Java object + thread_data_t *thread_data; // pointer to thread local data + // max 1 thread per sco socket +} native_data_t; + +/* thread local data */ +struct thread_data_t { + native_data_t *nat; + bool is_accept; // accept (listening) or connect (outgoing) thread + int signal_sk; // socket for thread to listen for unblock signal + char address[BTADDR_SIZE]; // BT addres as string +}; + +static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { + return (native_data_t *)(env->GetIntField(object, field_mNativeData)); +} +#endif + +static void classInitNative(JNIEnv* env, jclass clazz) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + if (env->GetJavaVM(&jvm) < 0) { + LOGE("Could not get handle to the VM"); + } + field_mNativeData = get_field(env, clazz, "mNativeData", "I"); + method_onAccepted = env->GetMethodID(clazz, "onAccepted", "(I)V"); + method_onConnected = env->GetMethodID(clazz, "onConnected", "(I)V"); + method_onClosed = env->GetMethodID(clazz, "onClosed", "()V"); +#endif +} + +/* Returns false if a serious error occured */ +static jboolean initNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + + native_data_t *nat = (native_data_t *) calloc(1, sizeof(native_data_t)); + if (nat == NULL) { + LOGE("%s: out of memory!", __FUNCTION__); + return JNI_FALSE; + } + + pthread_mutex_init(&nat->mutex, NULL); + env->SetIntField(object, field_mNativeData, (jint)nat); + nat->signal_sk = -1; + nat->object = NULL; + nat->thread_data = NULL; + +#endif + return JNI_TRUE; +} + +static void destroyNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + + closeNative(env, object); + + pthread_mutex_lock(&nat->mutex); + if (nat->thread_data != NULL) { + nat->thread_data->nat = NULL; + } + pthread_mutex_unlock(&nat->mutex); + pthread_mutex_destroy(&nat->mutex); + + free(nat); +#endif +} + +static jboolean acceptNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + int signal_sks[2]; + pthread_t thread; + struct thread_data_t *data = NULL; + + pthread_mutex_lock(&nat->mutex); + if (nat->signal_sk != -1) { + pthread_mutex_unlock(&nat->mutex); + return JNI_FALSE; + } + + // setup socketpair to pass messages between threads + if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_sks) < 0) { + LOGE("%s: socketpair() failed: %s", __FUNCTION__, strerror(errno)); + pthread_mutex_unlock(&nat->mutex); + return JNI_FALSE; + } + nat->signal_sk = signal_sks[0]; + nat->object = env->NewGlobalRef(object); + + data = (thread_data_t *)calloc(1, sizeof(thread_data_t)); + if (data == NULL) { + LOGE("%s: out of memory", __FUNCTION__); + pthread_mutex_unlock(&nat->mutex); + return JNI_FALSE; + } + nat->thread_data = data; + pthread_mutex_unlock(&nat->mutex); + + data->signal_sk = signal_sks[1]; + data->nat = nat; + data->is_accept = true; + + if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) { + LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno)); + return JNI_FALSE; + } + return JNI_TRUE; + +#endif + return JNI_FALSE; +} + +static jboolean connectNative(JNIEnv *env, jobject object, jstring address) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + int signal_sks[2]; + pthread_t thread; + struct thread_data_t *data; + const char *c_address; + + pthread_mutex_lock(&nat->mutex); + if (nat->signal_sk != -1) { + pthread_mutex_unlock(&nat->mutex); + return JNI_FALSE; + } + + // setup socketpair to pass messages between threads + if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_sks) < 0) { + LOGE("%s: socketpair() failed: %s\n", __FUNCTION__, strerror(errno)); + pthread_mutex_unlock(&nat->mutex); + return JNI_FALSE; + } + nat->signal_sk = signal_sks[0]; + nat->object = env->NewGlobalRef(object); + + data = (thread_data_t *)calloc(1, sizeof(thread_data_t)); + if (data == NULL) { + LOGE("%s: out of memory", __FUNCTION__); + pthread_mutex_unlock(&nat->mutex); + return JNI_FALSE; + } + pthread_mutex_unlock(&nat->mutex); + + data->signal_sk = signal_sks[1]; + data->nat = nat; + c_address = env->GetStringUTFChars(address, NULL); + strlcpy(data->address, c_address, BTADDR_SIZE); + env->ReleaseStringUTFChars(address, c_address); + data->is_accept = false; + + if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) { + LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno)); + return JNI_FALSE; + } + return JNI_TRUE; + +#endif + return JNI_FALSE; +} + +static void closeNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + int signal_sk; + + pthread_mutex_lock(&nat->mutex); + signal_sk = nat->signal_sk; + nat->signal_sk = -1; + env->DeleteGlobalRef(nat->object); + nat->object = NULL; + pthread_mutex_unlock(&nat->mutex); + + if (signal_sk >= 0) { + LOGV("%s: signal_sk = %d", __FUNCTION__, signal_sk); + unsigned char dummy; + write(signal_sk, &dummy, sizeof(dummy)); + close(signal_sk); + } +#endif +} + +#ifdef HAVE_BLUETOOTH +/* thread entry point */ +static void *work_thread(void *arg) { + JNIEnv* env; + thread_data_t *data = (thread_data_t *)arg; + int sk; + + LOGV(__FUNCTION__); + if (jvm->AttachCurrentThread(&env, NULL) != JNI_OK) { + LOGE("%s: AttachCurrentThread() failed", __FUNCTION__); + return NULL; + } + + /* connect the SCO socket */ + if (data->is_accept) { + LOGV("SCO OBJECT %p ACCEPT #####", data->nat->object); + sk = accept_work(data->signal_sk); + LOGV("SCO OBJECT %p END ACCEPT *****", data->nat->object); + } else { + sk = connect_work(data->address); + } + + /* callback with connection result */ + if (data->nat == NULL) { + LOGV("%s: object destroyed!", __FUNCTION__); + goto done; + } + pthread_mutex_lock(&data->nat->mutex); + if (data->nat->object == NULL) { + pthread_mutex_unlock(&data->nat->mutex); + LOGV("%s: callback cancelled", __FUNCTION__); + goto done; + } + if (data->is_accept) { + env->CallVoidMethod(data->nat->object, method_onAccepted, sk); + } else { + env->CallVoidMethod(data->nat->object, method_onConnected, sk); + } + pthread_mutex_unlock(&data->nat->mutex); + + if (sk < 0) { + goto done; + } + + LOGV("SCO OBJECT %p %d CONNECTED +++ (%s)", data->nat->object, sk, + data->is_accept ? "in" : "out"); + + /* wait for the socket to close */ + LOGV("wait_for_close()..."); + wait_for_close(sk, data->signal_sk); + LOGV("wait_for_close() returned"); + + /* callback with close result */ + if (data->nat == NULL) { + LOGV("%s: object destroyed!", __FUNCTION__); + goto done; + } + pthread_mutex_lock(&data->nat->mutex); + if (data->nat->object == NULL) { + LOGV("%s: callback cancelled", __FUNCTION__); + } else { + env->CallVoidMethod(data->nat->object, method_onClosed); + } + pthread_mutex_unlock(&data->nat->mutex); + +done: + if (sk >= 0) { + close(sk); + LOGV("SCO OBJECT %p %d CLOSED --- (%s)", data->nat->object, sk, data->is_accept ? "in" : "out"); + } + if (data->signal_sk >= 0) { + close(data->signal_sk); + } + LOGV("SCO socket closed"); + + if (data->nat != NULL) { + pthread_mutex_lock(&data->nat->mutex); + env->DeleteGlobalRef(data->nat->object); + data->nat->object = NULL; + data->nat->thread_data = NULL; + pthread_mutex_unlock(&data->nat->mutex); + } + + free(data); + if (jvm->DetachCurrentThread() != JNI_OK) { + LOGE("%s: DetachCurrentThread() failed", __FUNCTION__); + } + + LOGV("work_thread() done"); + return NULL; +} + +static int accept_work(int signal_sk) { + LOGV(__FUNCTION__); + int sk; + int nsk; + int addr_sz; + int max_fd; + fd_set fds; + struct sockaddr_sco addr; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); + if (sk < 0) { + LOGE("%s socket() failed: %s", __FUNCTION__, strerror(errno)); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + memcpy(&addr.sco_bdaddr, BDADDR_ANY, sizeof(bdaddr_t)); + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + LOGE("%s bind() failed: %s", __FUNCTION__, strerror(errno)); + goto error; + } + + if (listen(sk, 1)) { + LOGE("%s: listen() failed: %s", __FUNCTION__, strerror(errno)); + goto error; + } + + memset(&addr, 0, sizeof(addr)); + addr_sz = sizeof(addr); + + FD_ZERO(&fds); + FD_SET(sk, &fds); + FD_SET(signal_sk, &fds); + + max_fd = (sk > signal_sk) ? sk : signal_sk; + LOGI("Listening SCO socket..."); + while (select(max_fd + 1, &fds, NULL, NULL, NULL) < 0) { + if (errno != EINTR) { + LOGE("%s: select() failed: %s", __FUNCTION__, strerror(errno)); + goto error; + } + LOGV("%s: select() EINTR, retrying", __FUNCTION__); + } + LOGV("select() returned"); + if (FD_ISSET(signal_sk, &fds)) { + // signal to cancel listening + LOGV("cancelled listening socket, closing"); + goto error; + } + if (!FD_ISSET(sk, &fds)) { + LOGE("error: select() returned >= 0 with no fds set"); + goto error; + } + + nsk = accept(sk, (struct sockaddr *)&addr, &addr_sz); + if (nsk < 0) { + LOGE("%s: accept() failed: %s", __FUNCTION__, strerror(errno)); + goto error; + } + LOGI("Connected SCO socket (incoming)"); + close(sk); // The listening socket + + return nsk; + +error: + close(sk); + + return -1; +} + +static int connect_work(const char *address) { + LOGV(__FUNCTION__); + struct sockaddr_sco addr; + int sk = -1; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); + if (sk < 0) { + LOGE("%s: socket() failed: %s", __FUNCTION__, strerror(errno)); + return -1; + } + + /* Bind to local address */ + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + memcpy(&addr.sco_bdaddr, BDADDR_ANY, sizeof(bdaddr_t)); + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + LOGE("%s: bind() failed: %s", __FUNCTION__, strerror(errno)); + goto error; + } + + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + get_bdaddr(address, &addr.sco_bdaddr); + LOGI("Connecting to socket"); + while (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + if (errno != EINTR) { + LOGE("%s: connect() failed: %s", __FUNCTION__, strerror(errno)); + goto error; + } + LOGV("%s: connect() EINTR, retrying", __FUNCTION__); + } + LOGI("SCO socket connected (outgoing)"); + + return sk; + +error: + if (sk >= 0) close(sk); + return -1; +} + +static void wait_for_close(int sk, int signal_sk) { + LOGV(__FUNCTION__); + pollfd p[2]; + + memset(p, 0, 2 * sizeof(pollfd)); + p[0].fd = sk; + p[1].fd = signal_sk; + p[1].events = POLLIN | POLLPRI; + + LOGV("poll..."); + + while (poll(p, 2, -1) < 0) { // blocks + if (errno != EINTR) { + LOGE("%s: poll() failed: %s", __FUNCTION__, strerror(errno)); + break; + } + LOGV("%s: poll() EINTR, retrying", __FUNCTION__); + } + + LOGV("poll() returned"); +} +#endif + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void*)classInitNative}, + {"initNative", "()V", (void *)initNative}, + {"destroyNative", "()V", (void *)destroyNative}, + {"connectNative", "(Ljava/lang/String;)Z", (void *)connectNative}, + {"acceptNative", "()Z", (void *)acceptNative}, + {"closeNative", "()V", (void *)closeNative}, +}; + +int register_android_bluetooth_ScoSocket(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/bluetooth/ScoSocket", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp new file mode 100644 index 0000000..c81af1ce --- /dev/null +++ b/core/jni/android_bluetooth_common.cpp @@ -0,0 +1,423 @@ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "bluetooth_common.cpp" + +#include "android_bluetooth_common.h" +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <cutils/properties.h> + +#ifdef HAVE_BLUETOOTH +#include <dbus/dbus.h> +#endif + +namespace android { + +#ifdef HAVE_BLUETOOTH +jfieldID get_field(JNIEnv *env, jclass clazz, const char *member, + const char *mtype) { + jfieldID field = env->GetFieldID(clazz, member, mtype); + if (field == NULL) { + LOGE("Can't find member %s", member); + } + return field; +} + +typedef struct { + void (*user_cb)(DBusMessage *, void *); + void *user; + JNIEnv *env; +} dbus_async_call_t; + +void dbus_func_args_async_callback(DBusPendingCall *call, void *data) { + + dbus_async_call_t *req = (dbus_async_call_t *)data; + DBusMessage *msg; + + /* This is guaranteed to be non-NULL, because this function is called only + when once the remote method invokation returns. */ + msg = dbus_pending_call_steal_reply(call); + + if (msg) { + if (req->user_cb) { + // The user may not deref the message object. + req->user_cb(msg, req->user); + } + dbus_message_unref(msg); + } + + //dbus_message_unref(req->method); + dbus_pending_call_cancel(call); + dbus_pending_call_unref(call); + free(req); +} + +dbus_bool_t dbus_func_args_async_valist(JNIEnv *env, + DBusConnection *conn, + int timeout_ms, + void (*user_cb)(DBusMessage *, void *), + void *user, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + va_list args) { + DBusMessage *msg = NULL; + const char *name; + dbus_async_call_t *pending; + dbus_bool_t reply = FALSE; + + /* Compose the command */ + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, path, ifc, func); + + if (msg == NULL) { + LOGE("Could not allocate D-Bus message object!"); + goto done; + } + + /* append arguments */ + if (!dbus_message_append_args_valist(msg, first_arg_type, args)) { + LOGE("Could not append argument to method call!"); + goto done; + } + + /* Make the call. */ + pending = (dbus_async_call_t *)malloc(sizeof(dbus_async_call_t)); + if (pending) { + DBusPendingCall *call; + + pending->env = env; + pending->user_cb = user_cb; + pending->user = user; + //pending->method = msg; + + reply = dbus_connection_send_with_reply(conn, msg, + &call, + timeout_ms); + if (reply == TRUE) { + dbus_pending_call_set_notify(call, + dbus_func_args_async_callback, + pending, + NULL); + } + } + +done: + if (msg) dbus_message_unref(msg); + return reply; +} + +dbus_bool_t dbus_func_args_async(JNIEnv *env, + DBusConnection *conn, + int timeout_ms, + void (*reply)(DBusMessage *, void *), + void *user, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + ...) { + dbus_bool_t ret; + va_list lst; + va_start(lst, first_arg_type); + ret = dbus_func_args_async_valist(env, conn, + timeout_ms, + reply, user, + path, ifc, func, + first_arg_type, lst); + va_end(lst); + return ret; +} + +// If err is NULL, then any errors will be LOGE'd, and free'd and the reply +// will be NULL. +// If err is not NULL, then it is assumed that dbus_error_init was already +// called, and error's will be returned to the caller without logging. The +// return value is NULL iff an error was set. The client must free the error if +// set. +DBusMessage * dbus_func_args_timeout_valist(JNIEnv *env, + DBusConnection *conn, + int timeout_ms, + DBusError *err, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + va_list args) { + + DBusMessage *msg = NULL, *reply = NULL; + const char *name; + bool return_error = (err != NULL); + + if (!return_error) { + err = (DBusError*)malloc(sizeof(DBusError)); + dbus_error_init(err); + } + + /* Compose the command */ + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, path, ifc, func); + + if (msg == NULL) { + LOGE("Could not allocate D-Bus message object!"); + goto done; + } + + /* append arguments */ + if (!dbus_message_append_args_valist(msg, first_arg_type, args)) { + LOGE("Could not append argument to method call!"); + goto done; + } + + /* Make the call. */ + reply = dbus_connection_send_with_reply_and_block(conn, msg, timeout_ms, err); + if (!return_error && dbus_error_is_set(err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(err, msg); + } + +done: + if (!return_error) { + free(err); + } + if (msg) dbus_message_unref(msg); + return reply; +} + +DBusMessage * dbus_func_args_timeout(JNIEnv *env, + DBusConnection *conn, + int timeout_ms, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + ...) { + DBusMessage *ret; + va_list lst; + va_start(lst, first_arg_type); + ret = dbus_func_args_timeout_valist(env, conn, timeout_ms, NULL, + path, ifc, func, + first_arg_type, lst); + va_end(lst); + return ret; +} + +DBusMessage * dbus_func_args(JNIEnv *env, + DBusConnection *conn, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + ...) { + DBusMessage *ret; + va_list lst; + va_start(lst, first_arg_type); + ret = dbus_func_args_timeout_valist(env, conn, -1, NULL, + path, ifc, func, + first_arg_type, lst); + va_end(lst); + return ret; +} + +DBusMessage * dbus_func_args_error(JNIEnv *env, + DBusConnection *conn, + DBusError *err, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + ...) { + DBusMessage *ret; + va_list lst; + va_start(lst, first_arg_type); + ret = dbus_func_args_timeout_valist(env, conn, -1, err, + path, ifc, func, + first_arg_type, lst); + va_end(lst); + return ret; +} + +jint dbus_returns_int32(JNIEnv *env, DBusMessage *reply) { + + DBusError err; + jint ret = -1; + + dbus_error_init(&err); + if (!dbus_message_get_args(reply, &err, + DBUS_TYPE_INT32, &ret, + DBUS_TYPE_INVALID)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + } + dbus_message_unref(reply); + return ret; +} + +jint dbus_returns_uint32(JNIEnv *env, DBusMessage *reply) { + + DBusError err; + jint ret = -1; + + dbus_error_init(&err); + if (!dbus_message_get_args(reply, &err, + DBUS_TYPE_UINT32, &ret, + DBUS_TYPE_INVALID)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + } + dbus_message_unref(reply); + return ret; +} + +jstring dbus_returns_string(JNIEnv *env, DBusMessage *reply) { + + DBusError err; + jstring ret = NULL; + const char *name; + + dbus_error_init(&err); + if (dbus_message_get_args(reply, &err, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) { + ret = env->NewStringUTF(name); + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + } + dbus_message_unref(reply); + + return ret; +} + +jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply) { + DBusError err; + jboolean ret = JNI_FALSE; + dbus_bool_t val = FALSE; + + dbus_error_init(&err); + + /* Check the return value. */ + if (dbus_message_get_args(reply, &err, + DBUS_TYPE_BOOLEAN, &val, + DBUS_TYPE_INVALID)) { + ret = val == TRUE ? JNI_TRUE : JNI_FALSE; + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + } + + dbus_message_unref(reply); + return ret; +} + +jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) { + + DBusError err; + char **list; + int i, len; + jobjectArray strArray = NULL; + + dbus_error_init(&err); + if (dbus_message_get_args (reply, + &err, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, + &list, &len, + DBUS_TYPE_INVALID)) { + jclass stringClass; + jstring classNameStr; + + //LOGV("%s: there are %d elements in string array!", __FUNCTION__, len); + + stringClass = env->FindClass("java/lang/String"); + strArray = env->NewObjectArray(len, stringClass, NULL); + + for (i = 0; i < len; i++) { + //LOGV("%s: array[%d] = [%s]", __FUNCTION__, i, list[i]); + env->SetObjectArrayElement(strArray, i, + env->NewStringUTF(list[i])); + } + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + } + + dbus_message_unref(reply); + return strArray; +} + +jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply) { + + DBusError err; + int i, len; + jbyte *list; + jbyteArray byteArray = NULL; + + dbus_error_init(&err); + if (dbus_message_get_args(reply, &err, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &list, &len, + DBUS_TYPE_INVALID)) { + //LOGV("%s: there are %d elements in byte array!", __FUNCTION__, len); + byteArray = env->NewByteArray(len); + if (byteArray) + env->SetByteArrayRegion(byteArray, 0, len, list); + + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + } + + dbus_message_unref(reply); + return byteArray; +} + +void get_bdaddr(const char *str, bdaddr_t *ba) { + char *d = ((char *)ba) + 5, *endp; + int i; + for(i = 0; i < 6; i++) { + *d-- = strtol(str, &endp, 16); + if (*endp != ':' && i != 5) { + memset(ba, 0, sizeof(bdaddr_t)); + return; + } + str = endp + 1; + } +} + +void get_bdaddr_as_string(const bdaddr_t *ba, char *str) { + const uint8_t *b = (const uint8_t *)ba; + sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + b[5], b[4], b[3], b[2], b[1], b[0]); +} + +bool debug_no_encrypt() { + return false; +#if 0 + char value[PROPERTY_VALUE_MAX] = ""; + + property_get("debug.bt.no_encrypt", value, ""); + if (!strncmp("true", value, PROPERTY_VALUE_MAX) || + !strncmp("1", value, PROPERTY_VALUE_MAX)) { + LOGD("mandatory bluetooth encryption disabled"); + return true; + } else { + return false; + } +#endif +} +#endif + +} /* namespace android */ diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h new file mode 100644 index 0000000..e77cd30 --- /dev/null +++ b/core/jni/android_bluetooth_common.h @@ -0,0 +1,137 @@ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_BLUETOOTH_COMMON_H +#define ANDROID_BLUETOOTH_COMMON_H + +// Set to 0 to enable verbose bluetooth logging +#define LOG_NDEBUG 1 + +#include "jni.h" +#include "utils/Log.h" + +#include <errno.h> +#include <stdint.h> + +#ifdef HAVE_BLUETOOTH +#include <dbus/dbus.h> +#include <bluetooth/bluetooth.h> +#endif + +namespace android { + +#ifdef HAVE_BLUETOOTH +#define BLUEZ_DBUS_BASE_PATH "/org/bluez" +#define BLUEZ_DBUS_BASE_IFC "org.bluez" + +// It would be nicer to retrieve this from bluez using GetDefaultAdapter, +// but this is only possible when the adapter is up (and hcid is running). +// It is much easier just to hardcode bluetooth adapter to hci0 +#define BLUETOOTH_ADAPTER_HCI_NUM 0 +#define BLUEZ_ADAPTER_OBJECT_NAME BLUEZ_DBUS_BASE_PATH "/hci0" + +#define BTADDR_SIZE 18 // size of BT address character array (including null) + +jfieldID get_field(JNIEnv *env, + jclass clazz, + const char *member, + const char *mtype); + +// LOGE and free a D-Bus error +// Using #define so that __FUNCTION__ resolves usefully +#define LOG_AND_FREE_DBUS_ERROR_WITH_MSG(err, msg) \ + { LOGE("%s: D-Bus error in %s: %s (%s)", __FUNCTION__, \ + dbus_message_get_member((msg)), (err)->name, (err)->message); \ + dbus_error_free((err)); } +#define LOG_AND_FREE_DBUS_ERROR(err) \ + { LOGE("%s: D-Bus error: %s (%s)", __FUNCTION__, \ + (err)->name, (err)->message); \ + dbus_error_free((err)); } + +dbus_bool_t dbus_func_args_async_valist(JNIEnv *env, + DBusConnection *conn, + int timeout_ms, + void (*reply)(DBusMessage *, void *), + void *user, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + va_list args); + +dbus_bool_t dbus_func_args_async(JNIEnv *env, + DBusConnection *conn, + int timeout_ms, + void (*reply)(DBusMessage *, void *), + void *user, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + ...); + +DBusMessage * dbus_func_args(JNIEnv *env, + DBusConnection *conn, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + ...); + +DBusMessage * dbus_func_args_error(JNIEnv *env, + DBusConnection *conn, + DBusError *err, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + ...); + +DBusMessage * dbus_func_args_timeout(JNIEnv *env, + DBusConnection *conn, + int timeout_ms, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + ...); + +DBusMessage * dbus_func_args_timeout_valist(JNIEnv *env, + DBusConnection *conn, + int timeout_ms, + DBusError *err, + const char *path, + const char *ifc, + const char *func, + int first_arg_type, + va_list args); + +jint dbus_returns_int32(JNIEnv *env, DBusMessage *reply); +jint dbus_returns_uint32(JNIEnv *env, DBusMessage *reply); +jstring dbus_returns_string(JNIEnv *env, DBusMessage *reply); +jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply); +jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply); +jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply); + +void get_bdaddr(const char *str, bdaddr_t *ba); +void get_bdaddr_as_string(const bdaddr_t *ba, char *str); + +bool debug_no_encrypt(); + +#endif +} /* namespace android */ + +#endif/*ANDROID_BLUETOOTH_COMMON_H*/ diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp new file mode 100644 index 0000000..f19fbbf --- /dev/null +++ b/core/jni/android_database_CursorWindow.cpp @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "CursorWindow" + +#include <jni.h> +#include <JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> + +#include <utils/Log.h> +#include <utils/String8.h> +#include <utils/String16.h> + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "CursorWindow.h" +#include "sqlite3_exception.h" +#include "android_util_Binder.h" + + +namespace android { + +static jfieldID gWindowField; +static jfieldID gBufferField; +static jfieldID gSizeCopiedField; + +#define GET_WINDOW(env, object) ((CursorWindow *)env->GetIntField(object, gWindowField)) +#define SET_WINDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window)) +#define SET_BUFFER(env, object, buf) (env->SetObjectField(object, gBufferField, buf)) +#define SET_SIZE_COPIED(env, object, size) (env->SetIntField(object, gSizeCopiedField, size)) + +CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow) +{ + return GET_WINDOW(env, javaWindow); +} + +static void native_init_empty(JNIEnv * env, jobject object, jboolean localOnly) +{ + uint8_t * data; + size_t size; + CursorWindow * window; + + window = new CursorWindow(MAX_WINDOW_SIZE); + if (!window) { + jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object"); + return; + } + + if (!window->initBuffer(localOnly)) { + jniThrowException(env, "java/lang/IllegalStateException", "Couldn't init cursor window"); + delete window; + return; + } + +LOG_WINDOW("native_init_empty: window = %p", window); + SET_WINDOW(env, object, window); +} + +static void native_init_memory(JNIEnv * env, jobject object, jobject memObj) +{ + sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, memObj)); + if (memory == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder"); + return; + } + + CursorWindow * window = new CursorWindow(); + if (!window) { + jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object"); + return; + } + if (!window->setMemory(memory)) { + jniThrowException(env, "java/lang/RuntimeException", "No memory in memObj"); + delete window; + return; + } + +LOG_WINDOW("native_init_memory: numRows = %d, numColumns = %d, window = %p", window->getNumRows(), window->getNumColumns(), window); + SET_WINDOW(env, object, window); +} + +static jobject native_getBinder(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (window) { + sp<IMemory> memory = window->getMemory(); + if (memory != NULL) { + sp<IBinder> binder = memory->asBinder(); + return javaObjectForIBinder(env, binder); + } + } + return NULL; +} + +static void native_clear(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Clearing window %p", window); + if (window == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "clear() called after close()"); + return; + } + window->clear(); +} + +static void native_close(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (window) { +LOG_WINDOW("Closing window %p", window); + delete window; + SET_WINDOW(env, object, 0); + } +} + +static void throwExceptionWithRowCol(JNIEnv * env, jint row, jint column) +{ + char buf[100]; + snprintf(buf, sizeof(buf), "get field slot from row %d col %d failed", row, column); + jniThrowException(env, "java/lang/IllegalStateException", buf); +} + +static void throwUnknowTypeException(JNIEnv * env, jint type) +{ + char buf[80]; + snprintf(buf, sizeof(buf), "UNKNOWN type %d", type); + jniThrowException(env, "java/lang/IllegalStateException", buf); +} + +static jlong getLong_native(JNIEnv * env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return 0; + } + + uint8_t type = field.type; + if (type == FIELD_TYPE_INTEGER) { + int64_t value; + if (window->getLong(row, column, &value)) { + return value; + } + return 0; + } else if (type == FIELD_TYPE_STRING) { + uint32_t size = field.data.buffer.size; + if (size > 0) { +#if WINDOW_STORAGE_UTF8 + return strtoll((char const *)window->offsetToPtr(field.data.buffer.offset), NULL, 0); +#else + String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2); + char const * str = ascii.string(); + return strtoll(str, NULL, 0); +#endif + } else { + return 0; + } + } else if (type == FIELD_TYPE_FLOAT) { + double value; + if (window->getDouble(row, column, &value)) { + return value; + } + return 0; + } else if (type == FIELD_TYPE_NULL) { + return 0; + } else if (type == FIELD_TYPE_BLOB) { + throw_sqlite3_exception(env, "Unable to convert BLOB to long"); + return 0; + } else { + throwUnknowTypeException(env, type); + return 0; + } +} + +static jbyteArray getBlob_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + uint8_t type = field.type; + if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) { + jbyteArray byteArray = env->NewByteArray(field.data.buffer.size); + LOG_ASSERT(byteArray, "Native could not create new byte[]"); + env->SetByteArrayRegion(byteArray, 0, field.data.buffer.size, + (const jbyte*)window->offsetToPtr(field.data.buffer.offset)); + return byteArray; + } else if (type == FIELD_TYPE_INTEGER) { + throw_sqlite3_exception(env, "INTEGER data in getBlob_native "); + } else if (type == FIELD_TYPE_FLOAT) { + throw_sqlite3_exception(env, "FLOAT data in getBlob_native "); + } else if (type == FIELD_TYPE_NULL) { + // do nothing + } else { + throwUnknowTypeException(env, type); + } + return NULL; +} + +static jboolean isBlob_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL; +} + +static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + uint8_t type = field.type; + if (type == FIELD_TYPE_STRING) { + uint32_t size = field.data.buffer.size; + if (size > 0) { +#if WINDOW_STORAGE_UTF8 + // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string + String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); + return env->NewString((jchar const *)utf16.string(), utf16.size()); +#else + return env->NewString((jchar const *)window->offsetToPtr(field.data.buffer.offset), size / 2); +#endif + } else { + return env->NewStringUTF(""); + } + } else if (type == FIELD_TYPE_INTEGER) { + int64_t value; + if (window->getLong(row, column, &value)) { + char buf[32]; + snprintf(buf, sizeof(buf), "%lld", value); + return env->NewStringUTF(buf); + } + return NULL; + } else if (type == FIELD_TYPE_FLOAT) { + double value; + if (window->getDouble(row, column, &value)) { + char buf[32]; + snprintf(buf, sizeof(buf), "%g", value); + return env->NewStringUTF(buf); + } + return NULL; + } else if (type == FIELD_TYPE_NULL) { + return NULL; + } else if (type == FIELD_TYPE_BLOB) { + throw_sqlite3_exception(env, "Unable to convert BLOB to string"); + return NULL; + } else { + throwUnknowTypeException(env, type); + return NULL; + } +} + +/** + * Use this only to convert characters that are known to be within the + * 0-127 range for direct conversion to UTF-16 + */ +static jint charToJchar(const char* src, jchar* dst, jint bufferSize) +{ + int32_t len = strlen(src); + + if (bufferSize < len) { + len = bufferSize; + } + + for (int i = 0; i < len; i++) { + *dst++ = (*src++ & 0x7F); + } + return len; +} + +static jcharArray copyStringToBuffer_native(JNIEnv* env, jobject object, jint row, + jint column, jint bufferSize, jobject buf) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot"); + return NULL; + } + + jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField); + if (buffer == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null"); + return NULL; + } + jchar* dst = env->GetCharArrayElements(buffer, NULL); + uint8_t type = field.type; + uint32_t sizeCopied = 0; + jcharArray newArray = NULL; + if (type == FIELD_TYPE_STRING) { + uint32_t size = field.data.buffer.size; + if (size > 0) { +#if WINDOW_STORAGE_UTF8 + // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string + String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); + int32_t strSize = utf16.size(); + if (strSize > bufferSize || dst == NULL) { + newArray = env->NewCharArray(strSize); + env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string()); + } else { + memcpy(dst, (jchar const *)utf16.string(), strSize * 2); + } + sizeCopied = strSize; +#else + sizeCopied = size/2 + size % 2; + if (size > bufferSize * 2 || dst == NULL) { + newArray = env->NewCharArray(sizeCopied); + memcpy(newArray, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); + } else { + memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); + } +#endif + } + } else if (type == FIELD_TYPE_INTEGER) { + int64_t value; + if (window->getLong(row, column, &value)) { + char buf[32]; + int len; + snprintf(buf, sizeof(buf), "%lld", value); + jchar* dst = env->GetCharArrayElements(buffer, NULL); + sizeCopied = charToJchar(buf, dst, bufferSize); + } + } else if (type == FIELD_TYPE_FLOAT) { + double value; + if (window->getDouble(row, column, &value)) { + char tempbuf[32]; + snprintf(tempbuf, sizeof(tempbuf), "%g", value); + jchar* dst = env->GetCharArrayElements(buffer, NULL); + sizeCopied = charToJchar(tempbuf, dst, bufferSize); + } + } else if (type == FIELD_TYPE_NULL) { + } else if (type == FIELD_TYPE_BLOB) { + throw_sqlite3_exception(env, "Unable to convert BLOB to string"); + } else { + LOGE("Unknown field type %d", type); + throw_sqlite3_exception(env, "UNKNOWN type in copyStringToBuffer_native()"); + } + SET_SIZE_COPIED(env, buf, sizeCopied); + env->ReleaseCharArrayElements(buffer, dst, JNI_OK); + return newArray; +} + +static jdouble getDouble_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return 0.0; + } + + uint8_t type = field.type; + if (type == FIELD_TYPE_FLOAT) { + double value; + if (window->getDouble(row, column, &value)) { + return value; + } + return 0.0; + } else if (type == FIELD_TYPE_STRING) { + uint32_t size = field.data.buffer.size; + if (size > 0) { +#if WINDOW_STORAGE_UTF8 + return strtod((char const *)window->offsetToPtr(field.data.buffer.offset), NULL); +#else + String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2); + char const * str = ascii.string(); + return strtod(str, NULL); +#endif + } else { + return 0.0; + } + } else if (type == FIELD_TYPE_INTEGER) { + int64_t value; + if (window->getLong(row, column, &value)) { + return (double) value; + } + return 0.0; + } else if (type == FIELD_TYPE_NULL) { + return 0.0; + } else if (type == FIELD_TYPE_BLOB) { + throw_sqlite3_exception(env, "Unable to convert BLOB to double"); + return 0.0; + } else { + throwUnknowTypeException(env, type); + return 0.0; + } +} + +static jboolean isNull_native(JNIEnv* env, jobject object, jint row, jint column) +{ + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window); + + bool isNull; + if (window->getNull(row, column, &isNull)) { + return isNull; + } + + //TODO throw execption? + return true; +} + +static jint getNumRows(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); + return window->getNumRows(); +} + +static jboolean setNumColumns(JNIEnv * env, jobject object, jint columnNum) +{ + CursorWindow * window = GET_WINDOW(env, object); + return window->setNumColumns(columnNum); +} + +static jboolean allocRow(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); + return window->allocRow() != NULL; +} + +static jboolean putBlob_native(JNIEnv * env, jobject object, jbyteArray value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!value) { + LOG_WINDOW("How did a null value send to here"); + return false; + } + field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col); + if (fieldSlot == NULL) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } + + jint len = env->GetArrayLength(value); + int offset = window->alloc(len); + if (!offset) { + LOG_WINDOW("Failed allocating %u bytes", len); + return false; + } + jbyte * bytes = env->GetByteArrayElements(value, NULL); + window->copyIn(offset, (uint8_t const *)bytes, len); + + // This must be updated after the call to alloc(), since that + // may move the field around in the window + fieldSlot->type = FIELD_TYPE_BLOB; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = len; + env->ReleaseByteArrayElements(value, bytes, JNI_ABORT); + LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, col, len, offset); + return true; +} + +static jboolean putString_native(JNIEnv * env, jobject object, jstring value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!value) { + LOG_WINDOW("How did a null value send to here"); + return false; + } + field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col); + if (fieldSlot == NULL) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } + +#if WINDOW_STORAGE_UTF8 + int len = env->GetStringUTFLength(value) + 1; + char const * valStr = env->GetStringUTFChars(value, NULL); +#else + int len = env->GetStringLength(value); + // GetStringLength return number of chars and one char takes 2 bytes + len *= 2; + const jchar* valStr = env->GetStringChars(value, NULL); +#endif + if (!valStr) { + LOG_WINDOW("value can't be transfer to UTFChars"); + return false; + } + + int offset = window->alloc(len); + if (!offset) { + LOG_WINDOW("Failed allocating %u bytes", len); +#if WINDOW_STORAGE_UTF8 + env->ReleaseStringUTFChars(value, valStr); +#else + env->ReleaseStringChars(value, valStr); +#endif + return false; + } + + window->copyIn(offset, (uint8_t const *)valStr, len); + + // This must be updated after the call to alloc(), since that + // may move the field around in the window + fieldSlot->type = FIELD_TYPE_STRING; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = len; + + LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, col, len, offset); +#if WINDOW_STORAGE_UTF8 + env->ReleaseStringUTFChars(value, valStr); +#else + env->ReleaseStringChars(value, valStr); +#endif + + return true; +} + +static jboolean putLong_native(JNIEnv * env, jobject object, jlong value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!window->putLong(row, col, value)) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } + + LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, col, value); + + return true; +} + +static jboolean putDouble_native(JNIEnv * env, jobject object, jdouble value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!window->putDouble(row, col, value)) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } + + LOG_WINDOW("%d,%d is FLOAT %lf", row, col, value); + + return true; +} + +static jboolean putNull_native(JNIEnv * env, jobject object, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!window->putNull(row, col)) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } + + LOG_WINDOW("%d,%d is NULL", row, col); + + return true; +} + +// free the last row +static void freeLastRow(JNIEnv * env, jobject object) { + CursorWindow * window = GET_WINDOW(env, object); + window->freeLastRow(); +} + +static JNINativeMethod sMethods[] = +{ + /* name, signature, funcPtr */ + {"native_init", "(Z)V", (void *)native_init_empty}, + {"native_init", "(Landroid/os/IBinder;)V", (void *)native_init_memory}, + {"native_getBinder", "()Landroid/os/IBinder;", (void *)native_getBinder}, + {"native_clear", "()V", (void *)native_clear}, + {"close_native", "()V", (void *)native_close}, + {"getLong_native", "(II)J", (void *)getLong_native}, + {"getBlob_native", "(II)[B", (void *)getBlob_native}, + {"isBlob_native", "(II)Z", (void *)isBlob_native}, + {"getString_native", "(II)Ljava/lang/String;", (void *)getString_native}, + {"copyStringToBuffer_native", "(IIILandroid/database/CharArrayBuffer;)[C", (void *)copyStringToBuffer_native}, + {"getDouble_native", "(II)D", (void *)getDouble_native}, + {"isNull_native", "(II)Z", (void *)isNull_native}, + {"getNumRows_native", "()I", (void *)getNumRows}, + {"setNumColumns_native", "(I)Z", (void *)setNumColumns}, + {"allocRow_native", "()Z", (void *)allocRow}, + {"putBlob_native", "([BII)Z", (void *)putBlob_native}, + {"putString_native", "(Ljava/lang/String;II)Z", (void *)putString_native}, + {"putLong_native", "(JII)Z", (void *)putLong_native}, + {"putDouble_native", "(DII)Z", (void *)putDouble_native}, + {"freeLastRow_native", "()V", (void *)freeLastRow}, + {"putNull_native", "(II)Z", (void *)putNull_native}, +}; + +int register_android_database_CursorWindow(JNIEnv * env) +{ + jclass clazz; + + clazz = env->FindClass("android/database/CursorWindow"); + if (clazz == NULL) { + LOGE("Can't find android/database/CursorWindow"); + return -1; + } + + gWindowField = env->GetFieldID(clazz, "nWindow", "I"); + + if (gWindowField == NULL) { + LOGE("Error locating fields"); + return -1; + } + + clazz = env->FindClass("android/database/CharArrayBuffer"); + if (clazz == NULL) { + LOGE("Can't find android/database/CharArrayBuffer"); + return -1; + } + + gBufferField = env->GetFieldID(clazz, "data", "[C"); + + if (gBufferField == NULL) { + LOGE("Error locating fields data in CharArrayBuffer"); + return -1; + } + + gSizeCopiedField = env->GetFieldID(clazz, "sizeCopied", "I"); + + if (gSizeCopiedField == NULL) { + LOGE("Error locating fields sizeCopied in CharArrayBuffer"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow", + sMethods, NELEM(sMethods)); +} + +} // namespace android diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp new file mode 100644 index 0000000..66f0118 --- /dev/null +++ b/core/jni/android_database_SQLiteDatabase.cpp @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2006-2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "Database" + +#include <utils/Log.h> +#include <utils/String8.h> +#include <utils/String16.h> + +#include <jni.h> +#include <JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> + +#include <sqlite3.h> +#include <sqlite3_android.h> +#include <string.h> +#include <utils.h> +#include <ctype.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <netdb.h> +#include <sys/ioctl.h> + +#include "sqlite3_exception.h" + +#define UTF16_STORAGE 0 +#define INVALID_VERSION -1 +#define SQLITE_SOFT_HEAP_LIMIT (4 * 1024 * 1024) +#define ANDROID_TABLE "android_metadata" + +namespace android { + +enum { + OPEN_READWRITE = 0x00000000, + OPEN_READONLY = 0x00000001, + OPEN_READ_MASK = 0x00000001, + NO_LOCALIZED_COLLATORS = 0x00000010, + CREATE_IF_NECESSARY = 0x10000000 +}; + +static jfieldID offset_db_handle; + +/* public native void dbopen(String path, int flags, String locale); */ +static void dbopen(JNIEnv* env, jobject object, jstring pathString, jint flags) +{ + int err; + sqlite3 * handle = NULL; + sqlite3_stmt * statement = NULL; + char const * path8 = env->GetStringUTFChars(pathString, NULL); + int sqliteFlags; + + // convert our flags into the sqlite flags + if (flags & CREATE_IF_NECESSARY) { + sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + } else if (flags & OPEN_READONLY) { + sqliteFlags = SQLITE_OPEN_READONLY; + } else { + sqliteFlags = SQLITE_OPEN_READWRITE; + } + + err = sqlite3_open_v2(path8, &handle, sqliteFlags, NULL); + if (err != SQLITE_OK) { + LOGE("sqlite3_open_v2(\"%s\", &handle, %d, NULL) failed\n", path8, sqliteFlags); + throw_sqlite3_exception(env, handle); + goto done; + } + + // The soft heap limit prevents the page cache allocations from growing + // beyond the given limit, no matter what the max page cache sizes are + // set to. The limit does not, as of 3.5.0, affect any other allocations. + sqlite3_soft_heap_limit(SQLITE_SOFT_HEAP_LIMIT); + + // Set the default busy handler to retry for 1000ms and then return SQLITE_BUSY + err = sqlite3_busy_timeout(handle, 1000 /* ms */); + if (err != SQLITE_OK) { + LOGE("sqlite3_busy_timeout(handle, 1000) failed for \"%s\"\n", path8); + throw_sqlite3_exception(env, handle); + goto done; + } + +#ifdef DB_INTEGRITY_CHECK + static const char* integritySql = "pragma integrity_check(1);"; + err = sqlite3_prepare_v2(handle, integritySql, -1, &statement, NULL); + if (err != SQLITE_OK) { + LOGE("sqlite_prepare_v2(handle, \"%s\") failed for \"%s\"\n", integritySql, path8); + throw_sqlite3_exception(env, handle); + goto done; + } + + // first is OK or error message + err = sqlite3_step(statement); + if (err != SQLITE_ROW) { + LOGE("integrity check failed for \"%s\"\n", integritySql, path8); + throw_sqlite3_exception(env, handle); + goto done; + } else { + const char *text = (const char*)sqlite3_column_text(statement, 0); + if (strcmp(text, "ok") != 0) { + LOGE("integrity check failed for \"%s\": %s\n", integritySql, path8, text); + jniThrowException(env, "android/database/sqlite/SQLiteDatabaseCorruptException", text); + goto done; + } + } +#endif + + err = register_android_functions(handle, UTF16_STORAGE); + if (err) { + throw_sqlite3_exception(env, handle); + goto done; + } + + LOGV("Opened '%s' - %p\n", path8, handle); + env->SetIntField(object, offset_db_handle, (int) handle); + handle = NULL; // The caller owns the handle now. + +done: + // Release allocated resources + if (path8 != NULL) env->ReleaseStringUTFChars(pathString, path8); + if (statement != NULL) sqlite3_finalize(statement); + if (handle != NULL) sqlite3_close(handle); +} + +/* public native void close(); */ +static void dbclose(JNIEnv* env, jobject object) +{ + sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); + + if (handle != NULL) { + LOGV("Closing database: handle=%p\n", handle); + int result = sqlite3_close(handle); + if (result == SQLITE_OK) { + LOGV("Closed %p\n", handle); + env->SetIntField(object, offset_db_handle, 0); + } else { + // This can happen if sub-objects aren't closed first. Make sure the caller knows. + throw_sqlite3_exception(env, handle); + LOGE("sqlite3_close(%p) failed: %d\n", handle, result); + } + } +} + +/* public native void native_execSQL(String sql); */ +static void native_execSQL(JNIEnv* env, jobject object, jstring sqlString) +{ + int err; + int stepErr; + sqlite3_stmt * statement = NULL; + sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); + jchar const * sql = env->GetStringChars(sqlString, NULL); + jsize sqlLen = env->GetStringLength(sqlString); + + if (sql == NULL || sqlLen == 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "You must supply an SQL string"); + return; + } + + err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL); + + env->ReleaseStringChars(sqlString, sql); + + if (err != SQLITE_OK) { + char const * sql8 = env->GetStringUTFChars(sqlString, NULL); + LOGE("Failure %d (%s) on %p when preparing '%s'.\n", err, sqlite3_errmsg(handle), handle, sql8); + throw_sqlite3_exception(env, handle, sql8); + env->ReleaseStringUTFChars(sqlString, sql8); + return; + } + + stepErr = sqlite3_step(statement); + err = sqlite3_finalize(statement); + + if (stepErr != SQLITE_DONE) { + if (stepErr == SQLITE_ROW) { + throw_sqlite3_exception(env, "Queries cannot be performed using execSQL(), use query() instead."); + } else { + char const * sql8 = env->GetStringUTFChars(sqlString, NULL); + LOGE("Failure %d (%s) on %p when executing '%s'\n", err, sqlite3_errmsg(handle), handle, sql8); + throw_sqlite3_exception(env, handle, sql8); + env->ReleaseStringUTFChars(sqlString, sql8); + + } + } else IF_LOGV() { + char const * sql8 = env->GetStringUTFChars(sqlString, NULL); + LOGV("Success on %p when executing '%s'\n", handle, sql8); + env->ReleaseStringUTFChars(sqlString, sql8); + } +} + +/* native long lastInsertRow(); */ +static jlong lastInsertRow(JNIEnv* env, jobject object) +{ + sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); + + return sqlite3_last_insert_rowid(handle); +} + +/* native int lastChangeCount(); */ +static jint lastChangeCount(JNIEnv* env, jobject object) +{ + sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); + + return sqlite3_changes(handle); +} + +/* set locale in the android_metadata table, install localized collators, and rebuild indexes */ +static void native_setLocale(JNIEnv* env, jobject object, jstring localeString, jint flags) +{ + if ((flags & NO_LOCALIZED_COLLATORS)) return; + + int err; + char const* locale8 = env->GetStringUTFChars(localeString, NULL); + sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); + sqlite3_stmt* stmt = NULL; + char** meta = NULL; + int rowCount, colCount; + char* dbLocale = NULL; + + // create the table, if necessary and possible + if (!(flags & OPEN_READONLY)) { + static const char *createSql ="CREATE TABLE IF NOT EXISTS " ANDROID_TABLE " (locale TEXT)"; + err = sqlite3_exec(handle, createSql, NULL, NULL, NULL); + if (err != SQLITE_OK) { + LOGE("CREATE TABLE " ANDROID_TABLE " failed\n"); + throw_sqlite3_exception(env, handle); + goto done; + } + } + + // try to read from the table + static const char *selectSql = "SELECT locale FROM " ANDROID_TABLE " LIMIT 1"; + err = sqlite3_get_table(handle, selectSql, &meta, &rowCount, &colCount, NULL); + if (err != SQLITE_OK) { + LOGE("SELECT locale FROM " ANDROID_TABLE " failed\n"); + throw_sqlite3_exception(env, handle); + goto done; + } + + dbLocale = (rowCount >= 1) ? meta[1 * colCount + 0] : NULL; + + if (dbLocale != NULL && !strcmp(dbLocale, locale8)) { + // database locale is the same as the desired locale; set up the collators and go + err = register_localized_collators(handle, locale8, UTF16_STORAGE); + if (err != SQLITE_OK) throw_sqlite3_exception(env, handle); + goto done; // no database changes needed + } + + if ((flags & OPEN_READONLY)) { + // read-only database, so we're going to have to put up with whatever we got + err = register_localized_collators(handle, dbLocale ? dbLocale : locale8, UTF16_STORAGE); + if (err != SQLITE_OK) throw_sqlite3_exception(env, handle); + goto done; + } + + // need to update android_metadata and indexes atomically, so use a transaction... + err = sqlite3_exec(handle, "BEGIN TRANSACTION", NULL, NULL, NULL); + if (err != SQLITE_OK) { + LOGE("BEGIN TRANSACTION failed setting locale\n"); + throw_sqlite3_exception(env, handle); + goto done; + } + + err = register_localized_collators(handle, dbLocale ? dbLocale : locale8, UTF16_STORAGE); + if (err != SQLITE_OK) { + LOGE("register_localized_collators() failed setting locale\n"); + throw_sqlite3_exception(env, handle); + goto done; + } + + err = sqlite3_exec(handle, "DELETE FROM " ANDROID_TABLE, NULL, NULL, NULL); + if (err != SQLITE_OK) { + LOGE("DELETE failed setting locale\n"); + throw_sqlite3_exception(env, handle); + goto rollback; + } + + static const char *sql = "INSERT INTO " ANDROID_TABLE " (locale) VALUES(?);"; + err = sqlite3_prepare_v2(handle, sql, -1, &stmt, NULL); + if (err != SQLITE_OK) { + LOGE("sqlite3_prepare_v2(\"%s\") failed\n", sql); + throw_sqlite3_exception(env, handle); + goto rollback; + } + + err = sqlite3_bind_text(stmt, 1, locale8, -1, SQLITE_TRANSIENT); + if (err != SQLITE_OK) { + LOGE("sqlite3_bind_text() failed setting locale\n"); + throw_sqlite3_exception(env, handle); + goto rollback; + } + + err = sqlite3_step(stmt); + if (err != SQLITE_OK && err != SQLITE_DONE) { + LOGE("sqlite3_step(\"%s\") failed setting locale\n", sql); + throw_sqlite3_exception(env, handle); + goto rollback; + } + + err = sqlite3_exec(handle, "REINDEX LOCALIZED", NULL, NULL, NULL); + if (err != SQLITE_OK) { + LOGE("REINDEX LOCALIZED failed\n"); + throw_sqlite3_exception(env, handle); + goto rollback; + } + + // all done, yay! + err = sqlite3_exec(handle, "COMMIT TRANSACTION", NULL, NULL, NULL); + if (err != SQLITE_OK) { + LOGE("COMMIT TRANSACTION failed setting locale\n"); + throw_sqlite3_exception(env, handle); + goto done; + } + +rollback: + sqlite3_exec(handle, "ROLLBACK TRANSACTION", NULL, NULL, NULL); + +done: + if (locale8 != NULL) env->ReleaseStringUTFChars(localeString, locale8); + if (stmt != NULL) sqlite3_finalize(stmt); + if (meta != NULL) sqlite3_free_table(meta); +} + +static jint native_releaseMemory(JNIEnv *env, jobject clazz) +{ + // Attempt to release as much memory from the + return sqlite3_release_memory(SQLITE_SOFT_HEAP_LIMIT); +} + +static JNINativeMethod sMethods[] = +{ + /* name, signature, funcPtr */ + {"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen}, + {"dbclose", "()V", (void *)dbclose}, + {"native_execSQL", "(Ljava/lang/String;)V", (void *)native_execSQL}, + {"lastInsertRow", "()J", (void *)lastInsertRow}, + {"lastChangeCount", "()I", (void *)lastChangeCount}, + {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale}, + {"releaseMemory", "()I", (void *)native_releaseMemory}, +}; + +int register_android_database_SQLiteDatabase(JNIEnv *env) +{ + jclass clazz; + + clazz = env->FindClass("android/database/sqlite/SQLiteDatabase"); + if (clazz == NULL) { + LOGE("Can't find android/database/sqlite/SQLiteDatabase\n"); + return -1; + } + + offset_db_handle = env->GetFieldID(clazz, "mNativeHandle", "I"); + if (offset_db_handle == NULL) { + LOGE("Can't find SQLiteDatabase.mNativeHandle\n"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase", sMethods, NELEM(sMethods)); +} + +/* throw a SQLiteException with a message appropriate for the error in handle */ +void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) { + throw_sqlite3_exception(env, handle, NULL); +} + +/* throw a SQLiteException with the given message */ +void throw_sqlite3_exception(JNIEnv* env, const char* message) { + throw_sqlite3_exception(env, NULL, message); +} + +/* throw a SQLiteException with a message appropriate for the error in handle + concatenated with the given message + */ +void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) { + if (handle) { + throw_sqlite3_exception(env, sqlite3_errcode(handle), + sqlite3_errmsg(handle), message); + } else { + // we use SQLITE_OK so that a generic SQLiteException is thrown; + // any code not specified in the switch statement below would do. + throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message); + } +} + +/* throw a SQLiteException for a given error code */ +void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) { + if (errcode == SQLITE_DONE) { + throw_sqlite3_exception(env, errcode, NULL, message); + } else { + char temp[20]; + sprintf(temp, "error code %d", errcode); + throw_sqlite3_exception(env, errcode, temp, message); + } +} + +/* throw a SQLiteException for a given error code, sqlite3message, and + user message + */ +void throw_sqlite3_exception(JNIEnv* env, int errcode, + const char* sqlite3Message, const char* message) { + const char* exceptionClass; + switch (errcode) { + case SQLITE_IOERR: + exceptionClass = "android/database/sqlite/SQLiteDiskIOException"; + break; + case SQLITE_CORRUPT: + exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException"; + break; + case SQLITE_CONSTRAINT: + exceptionClass = "android/database/sqlite/SQLiteConstraintException"; + break; + case SQLITE_ABORT: + exceptionClass = "android/database/sqlite/SQLiteAbortException"; + break; + case SQLITE_DONE: + exceptionClass = "android/database/sqlite/SQLiteDoneException"; + break; + case SQLITE_FULL: + exceptionClass = "android/database/sqlite/SQLiteFullException"; + break; + case SQLITE_MISUSE: + exceptionClass = "android/database/sqlite/SQLiteMisuseException"; + break; + default: + exceptionClass = "android/database/sqlite/SQLiteException"; + break; + } + + if (sqlite3Message != NULL && message != NULL) { + char* fullMessage = (char *)malloc(strlen(sqlite3Message) + strlen(message) + 3); + if (fullMessage != NULL) { + strcpy(fullMessage, sqlite3Message); + strcat(fullMessage, ": "); + strcat(fullMessage, message); + jniThrowException(env, exceptionClass, fullMessage); + free(fullMessage); + } else { + jniThrowException(env, exceptionClass, sqlite3Message); + } + } else if (sqlite3Message != NULL) { + jniThrowException(env, exceptionClass, sqlite3Message); + } else { + jniThrowException(env, exceptionClass, message); + } +} + + +} // namespace android diff --git a/core/jni/android_database_SQLiteDebug.cpp b/core/jni/android_database_SQLiteDebug.cpp new file mode 100644 index 0000000..916df35 --- /dev/null +++ b/core/jni/android_database_SQLiteDebug.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <JNIHelp.h> +#include <jni.h> +#include <utils/misc.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <cutils/mspace.h> +#include <utils/Log.h> + +#include <sqlite3.h> + +// From mem_mspace.c in libsqlite +extern "C" mspace sqlite3_get_mspace(); + +// From sqlite.c, hacked in for Android +extern "C" void sqlite3_get_pager_stats(sqlite3_int64 * totalBytesOut, + sqlite3_int64 * referencedBytesOut, + sqlite3_int64 * dbBytesOut, + int * numPagersOut); + +namespace android { + +static jfieldID gTotalBytesField; +static jfieldID gReferencedBytesField; +static jfieldID gDbBytesField; +static jfieldID gNumPagersField; + + +#define USE_MSPACE 0 + +static void getPagerStats(JNIEnv *env, jobject clazz, jobject statsObj) +{ + sqlite3_int64 totalBytes; + sqlite3_int64 referencedBytes; + sqlite3_int64 dbBytes; + int numPagers; + + sqlite3_get_pager_stats(&totalBytes, &referencedBytes, &dbBytes, + &numPagers); + + env->SetLongField(statsObj, gTotalBytesField, totalBytes); + env->SetLongField(statsObj, gReferencedBytesField, referencedBytes); + env->SetLongField(statsObj, gDbBytesField, dbBytes); + env->SetIntField(statsObj, gNumPagersField, numPagers); +} + +static jlong getHeapSize(JNIEnv *env, jobject clazz) +{ +#if !NO_MALLINFO + struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace()); + struct mallinfo info = dlmallinfo(); + return (jlong) info.usmblks; +#elif USE_MSPACE + mspace space = sqlite3_get_mspace(); + if (space != 0) { + return mspace_footprint(space); + } else { + return 0; + } +#else + return 0; +#endif +} + +static jlong getHeapAllocatedSize(JNIEnv *env, jobject clazz) +{ +#if !NO_MALLINFO + struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace()); + return (jlong) info.uordblks; +#else + return sqlite3_memory_used(); +#endif +} + +static jlong getHeapFreeSize(JNIEnv *env, jobject clazz) +{ +#if !NO_MALLINFO + struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace()); + return (jlong) info.fordblks; +#else + return getHeapSize(env, clazz) - sqlite3_memory_used(); +#endif +} + +static int read_mapinfo(FILE *fp, + int *sharedPages, int *privatePages) +{ + char line[1024]; + int len; + int skip; + + unsigned start = 0, size = 0, resident = 0; + unsigned shared_clean = 0, shared_dirty = 0; + unsigned private_clean = 0, private_dirty = 0; + unsigned referenced = 0; + + int isAnon = 0; + int isHeap = 0; + +again: + skip = 0; + + if(fgets(line, 1024, fp) == 0) return 0; + + len = strlen(line); + if (len < 1) return 0; + line[--len] = 0; + + /* ignore guard pages */ + if (line[18] == '-') skip = 1; + + start = strtoul(line, 0, 16); + + if (len > 50 && !strncmp(line + 49, "/tmp/sqlite-heap", strlen("/tmp/sqlite-heap"))) { + isHeap = 1; + } + + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Size: %d kB", &size) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Rss: %d kB", &resident) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Shared_Clean: %d kB", &shared_clean) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Shared_Dirty: %d kB", &shared_dirty) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Private_Clean: %d kB", &private_clean) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Private_Dirty: %d kB", &private_dirty) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Referenced: %d kB", &referenced) != 1) return 0; + + if (skip) { + goto again; + } + + if (isHeap) { + *sharedPages += shared_dirty; + *privatePages += private_dirty; + } + return 1; +} + +static void load_maps(int pid, int *sharedPages, int *privatePages) +{ + char tmp[128]; + FILE *fp; + + sprintf(tmp, "/proc/%d/smaps", pid); + fp = fopen(tmp, "r"); + if (fp == 0) return; + + while (read_mapinfo(fp, sharedPages, privatePages) != 0) { + // Do nothing + } + fclose(fp); +} + +static void getHeapDirtyPages(JNIEnv *env, jobject clazz, jintArray pages) +{ + int _pages[2]; + + _pages[0] = 0; + _pages[1] = 0; + + load_maps(getpid(), &_pages[0], &_pages[1]); + + // Convert from kbytes to 4K pages + _pages[0] /= 4; + _pages[1] /= 4; + + env->SetIntArrayRegion(pages, 0, 2, _pages); +} + +/* + * JNI registration. + */ + +static JNINativeMethod gMethods[] = +{ + { "getPagerStats", "(Landroid/database/sqlite/SQLiteDebug$PagerStats;)V", + (void*) getPagerStats }, + { "getHeapSize", "()J", (void*) getHeapSize }, + { "getHeapAllocatedSize", "()J", (void*) getHeapAllocatedSize }, + { "getHeapFreeSize", "()J", (void*) getHeapFreeSize }, + { "getHeapDirtyPages", "([I)V", (void*) getHeapDirtyPages }, +}; + +int register_android_database_SQLiteDebug(JNIEnv *env) +{ + jclass clazz; + + clazz = env->FindClass("android/database/sqlite/SQLiteDebug$PagerStats"); + if (clazz == NULL) { + LOGE("Can't find android/database/sqlite/SQLiteDebug$PagerStats"); + return -1; + } + + gTotalBytesField = env->GetFieldID(clazz, "totalBytes", "J"); + if (gTotalBytesField == NULL) { + LOGE("Can't find totalBytes"); + return -1; + } + + gReferencedBytesField = env->GetFieldID(clazz, "referencedBytes", "J"); + if (gReferencedBytesField == NULL) { + LOGE("Can't find referencedBytes"); + return -1; + } + + gDbBytesField = env->GetFieldID(clazz, "databaseBytes", "J"); + if (gDbBytesField == NULL) { + LOGE("Can't find databaseBytes"); + return -1; + } + + gNumPagersField = env->GetFieldID(clazz, "numPagers", "I"); + if (gNumPagersField == NULL) { + LOGE("Can't find numPagers"); + return -1; + } + + return jniRegisterNativeMethods(env, "android/database/sqlite/SQLiteDebug", + gMethods, NELEM(gMethods)); +} + +} // namespace android diff --git a/core/jni/android_database_SQLiteProgram.cpp b/core/jni/android_database_SQLiteProgram.cpp new file mode 100644 index 0000000..54e7de2 --- /dev/null +++ b/core/jni/android_database_SQLiteProgram.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2006-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. + */ + +#undef LOG_TAG +#define LOG_TAG "Cursor" + +#include <jni.h> +#include <JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> + +#include <sqlite3.h> + +#include <utils/Log.h> + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "sqlite3_exception.h" + + +namespace android { + +static jfieldID gHandleField; +static jfieldID gStatementField; + + +#define GET_STATEMENT(env, object) \ + (sqlite3_stmt *)env->GetIntField(object, gStatementField) +#define GET_HANDLE(env, object) \ + (sqlite3 *)env->GetIntField(object, gHandleField) + + +sqlite3_stmt * compile(JNIEnv* env, jobject object, + sqlite3 * handle, jstring sqlString) +{ + int err; + jchar const * sql; + jsize sqlLen; + sqlite3_stmt * statement = GET_STATEMENT(env, object); + + // Make sure not to leak the statement if it already exists + if (statement != NULL) { + sqlite3_finalize(statement); + env->SetIntField(object, gStatementField, 0); + } + + // Compile the SQL + sql = env->GetStringChars(sqlString, NULL); + sqlLen = env->GetStringLength(sqlString); + err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL); + env->ReleaseStringChars(sqlString, sql); + + if (err == SQLITE_OK) { + // Store the statement in the Java object for future calls + LOGV("Prepared statement %p on %p", statement, handle); + env->SetIntField(object, gStatementField, (int)statement); + return statement; + } else { + // Error messages like 'near ")": syntax error' are not + // always helpful enough, so construct an error string that + // includes the query itself. + const char *query = env->GetStringUTFChars(sqlString, NULL); + char *message = (char*) malloc(strlen(query) + 50); + if (message) { + strcpy(message, ", while compiling: "); // less than 50 chars + strcat(message, query); + } + env->ReleaseStringUTFChars(sqlString, query); + throw_sqlite3_exception(env, handle, message); + free(message); + return NULL; + } +} + +static void native_compile(JNIEnv* env, jobject object, jstring sqlString) +{ + compile(env, object, GET_HANDLE(env, object), sqlString); +} + +static void native_bind_null(JNIEnv* env, jobject object, + jint index) +{ + int err; + sqlite3_stmt * statement = GET_STATEMENT(env, object); + + err = sqlite3_bind_null(statement, index); + if (err != SQLITE_OK) { + char buf[32]; + sprintf(buf, "handle %p", statement); + throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); + return; + } +} + +static void native_bind_long(JNIEnv* env, jobject object, + jint index, jlong value) +{ + int err; + sqlite3_stmt * statement = GET_STATEMENT(env, object); + + err = sqlite3_bind_int64(statement, index, value); + if (err != SQLITE_OK) { + char buf[32]; + sprintf(buf, "handle %p", statement); + throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); + return; + } +} + +static void native_bind_double(JNIEnv* env, jobject object, + jint index, jdouble value) +{ + int err; + sqlite3_stmt * statement = GET_STATEMENT(env, object); + + err = sqlite3_bind_double(statement, index, value); + if (err != SQLITE_OK) { + char buf[32]; + sprintf(buf, "handle %p", statement); + throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); + return; + } +} + +static void native_bind_string(JNIEnv* env, jobject object, + jint index, jstring sqlString) +{ + int err; + jchar const * sql; + jsize sqlLen; + sqlite3_stmt * statement= GET_STATEMENT(env, object); + + sql = env->GetStringChars(sqlString, NULL); + sqlLen = env->GetStringLength(sqlString); + err = sqlite3_bind_text16(statement, index, sql, sqlLen * 2, SQLITE_TRANSIENT); + env->ReleaseStringChars(sqlString, sql); + if (err != SQLITE_OK) { + char buf[32]; + sprintf(buf, "handle %p", statement); + throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); + return; + } +} + +static void native_bind_blob(JNIEnv* env, jobject object, + jint index, jbyteArray value) +{ + int err; + jchar const * sql; + jsize sqlLen; + sqlite3_stmt * statement= GET_STATEMENT(env, object); + + jint len = env->GetArrayLength(value); + jbyte * bytes = env->GetByteArrayElements(value, NULL); + + err = sqlite3_bind_blob(statement, index, bytes, len, SQLITE_TRANSIENT); + env->ReleaseByteArrayElements(value, bytes, JNI_ABORT); + + if (err != SQLITE_OK) { + char buf[32]; + sprintf(buf, "handle %p", statement); + throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); + return; + } +} + +static void native_clear_bindings(JNIEnv* env, jobject object) +{ + int err; + sqlite3_stmt * statement = GET_STATEMENT(env, object); + + err = sqlite3_clear_bindings(statement); + if (err != SQLITE_OK) { + throw_sqlite3_exception(env, GET_HANDLE(env, object)); + return; + } +} + +static void native_finalize(JNIEnv* env, jobject object) +{ + int err; + sqlite3_stmt * statement = GET_STATEMENT(env, object); + + if (statement != NULL) { + sqlite3_finalize(statement); + env->SetIntField(object, gStatementField, 0); + } +} + + +static JNINativeMethod sMethods[] = +{ + /* name, signature, funcPtr */ + {"native_compile", "(Ljava/lang/String;)V", (void *)native_compile}, + {"native_bind_null", "(I)V", (void *)native_bind_null}, + {"native_bind_long", "(IJ)V", (void *)native_bind_long}, + {"native_bind_double", "(ID)V", (void *)native_bind_double}, + {"native_bind_string", "(ILjava/lang/String;)V", (void *)native_bind_string}, + {"native_bind_blob", "(I[B)V", (void *)native_bind_blob}, + {"native_clear_bindings", "()V", (void *)native_clear_bindings}, + {"native_finalize", "()V", (void *)native_finalize}, +}; + +int register_android_database_SQLiteProgram(JNIEnv * env) +{ + jclass clazz; + + clazz = env->FindClass("android/database/sqlite/SQLiteProgram"); + if (clazz == NULL) { + LOGE("Can't find android/database/sqlite/SQLiteProgram"); + return -1; + } + + gHandleField = env->GetFieldID(clazz, "nHandle", "I"); + gStatementField = env->GetFieldID(clazz, "nStatement", "I"); + + if (gHandleField == NULL || gStatementField == NULL) { + LOGE("Error locating fields"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + "android/database/sqlite/SQLiteProgram", sMethods, NELEM(sMethods)); +} + +} // namespace android diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp new file mode 100644 index 0000000..9458433 --- /dev/null +++ b/core/jni/android_database_SQLiteQuery.cpp @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "Cursor" + +#include <jni.h> +#include <JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> + +#include <sqlite3.h> + +#include <utils/Log.h> + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "CursorWindow.h" +#include "sqlite3_exception.h" + + +namespace android { + +sqlite3_stmt * compile(JNIEnv* env, jobject object, + sqlite3 * handle, jstring sqlString); + +// From android_database_CursorWindow.cpp +CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow); + +static jfieldID gHandleField; +static jfieldID gStatementField; + + +#define GET_STATEMENT(env, object) \ + (sqlite3_stmt *)env->GetIntField(object, gStatementField) +#define GET_HANDLE(env, object) \ + (sqlite3 *)env->GetIntField(object, gHandleField) + +static int skip_rows(sqlite3_stmt *statement, int maxRows) { + int retryCount = 0; + for (int i = 0; i < maxRows; i++) { + int err = sqlite3_step(statement); + if (err == SQLITE_ROW){ + // do nothing + } else if (err == SQLITE_DONE) { + return i; + } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) { + // The table is locked, retry + LOG_WINDOW("Database locked, retrying"); + if (retryCount > 50) { + LOGE("Bailing on database busy rety"); + break; + } + // Sleep to give the thread holding the lock a chance to finish + usleep(1000); + retryCount++; + continue; + } else { + return -1; + } + } + LOGD("skip_rows row %d", maxRows); + return maxRows; +} + +static int finish_program_and_get_row_count(sqlite3_stmt *statement) { + int numRows = 0; + int retryCount = 0; + while (true) { + int err = sqlite3_step(statement); + if (err == SQLITE_ROW){ + numRows++; + } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) { + // The table is locked, retry + LOG_WINDOW("Database locked, retrying"); + if (retryCount > 50) { + LOGE("Bailing on database busy rety"); + break; + } + // Sleep to give the thread holding the lock a chance to finish + usleep(1000); + retryCount++; + continue; + } else { + // no need to throw exception + break; + } + } + sqlite3_reset(statement); + LOGD("finish_program_and_get_row_count row %d", numRows); + return numRows; +} + +static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, + jint startPos, jint offsetParam) +{ + int err; + sqlite3_stmt * statement = GET_STATEMENT(env, object); + int numRows = 0; + int numColumns; + int retryCount; + int boundParams; + CursorWindow * window; + + if (statement == NULL) { + LOGE("Invalid statement in fillWindow()"); + jniThrowException(env, "java/lang/IllegalStateException", + "Attempting to access a deactivated, closed, or empty cursor"); + return 0; + } + + // Only do the binding if there is a valid offsetParam. If no binding needs to be done + // offsetParam will be set to 0, an invliad value. + if(offsetParam > 0) { + // Bind the offset parameter, telling the program which row to start with + err = sqlite3_bind_int(statement, offsetParam, startPos); + if (err != SQLITE_OK) { + LOGE("Unable to bind offset position, offsetParam = %d", offsetParam); + jniThrowException(env, "java/lang/IllegalArgumentException", + sqlite3_errmsg(GET_HANDLE(env, object))); + return 0; + } + LOG_WINDOW("Bound to startPos %d", startPos); + } else { + LOG_WINDOW("Not binding to startPos %d", startPos); + } + + // Get the native window + window = get_window_from_object(env, javaWindow); + if (!window) { + LOGE("Invalid CursorWindow"); + jniThrowException(env, "java/lang/IllegalArgumentException", + "Bad CursorWindow"); + return 0; + } + LOG_WINDOW("Window: numRows = %d, size = %d, freeSpace = %d", window->getNumRows(), window->size(), window->freeSpace()); + + numColumns = sqlite3_column_count(statement); + if (!window->setNumColumns(numColumns)) { + LOGE("Failed to change column count from %d to %d", window->getNumColumns(), numColumns); + jniThrowException(env, "java/lang/IllegalStateException", "numColumns mismatch"); + return 0; + } + + retryCount = 0; + if (startPos > 0) { + int num = skip_rows(statement, startPos); + if (num < 0) { + throw_sqlite3_exception(env, GET_HANDLE(env, object)); + return 0; + } else if (num < startPos) { + LOGE("startPos %d > actual rows %d", startPos, num); + return num; + } + } + + while(true) { + err = sqlite3_step(statement); + if (err == SQLITE_ROW) { + LOG_WINDOW("\nStepped statement %p to row %d", statement, startPos + numRows); + retryCount = 0; + + // Allocate a new field directory for the row. This pointer is not reused + // since it mey be possible for it to be relocated on a call to alloc() when + // the field data is being allocated. + { + field_slot_t * fieldDir = window->allocRow(); + if (!fieldDir) { + LOGE("Failed allocating fieldDir at startPos %d row %d", startPos, numRows); + return startPos + numRows + finish_program_and_get_row_count(statement) + 1; + } + } + + // Pack the row into the window + int i; + for (i = 0; i < numColumns; i++) { + int type = sqlite3_column_type(statement, i); + if (type == SQLITE_TEXT) { + // TEXT data +#if WINDOW_STORAGE_UTF8 + uint8_t const * text = (uint8_t const *)sqlite3_column_text(statement, i); + // SQLite does not include the NULL terminator in size, but does + // ensure all strings are NULL terminated, so increase size by + // one to make sure we store the terminator. + size_t size = sqlite3_column_bytes(statement, i) + 1; +#else + uint8_t const * text = (uint8_t const *)sqlite3_column_text16(statement, i); + size_t size = sqlite3_column_bytes16(statement, i); +#endif + int offset = window->alloc(size); + if (!offset) { + window->freeLastRow(); + LOGE("Failed allocating %u bytes for text/blob at %d,%d", size, + startPos + numRows, i); + return startPos + numRows + finish_program_and_get_row_count(statement) + 1; + } + + window->copyIn(offset, text, size); + + // This must be updated after the call to alloc(), since that + // may move the field around in the window + field_slot_t * fieldSlot = window->getFieldSlot(numRows, i); + fieldSlot->type = FIELD_TYPE_STRING; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = size; + + LOG_WINDOW("%d,%d is TEXT with %u bytes", startPos + numRows, i, size); + } else if (type == SQLITE_INTEGER) { + // INTEGER data + int64_t value = sqlite3_column_int64(statement, i); + if (!window->putLong(numRows, i, value)) { + window->freeLastRow(); + LOGE("Failed allocating space for a long in column %d", i); + return startPos + numRows + finish_program_and_get_row_count(statement) + 1; + } + LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + numRows, i, value); + } else if (type == SQLITE_FLOAT) { + // FLOAT data + double value = sqlite3_column_double(statement, i); + if (!window->putDouble(numRows, i, value)) { + window->freeLastRow(); + LOGE("Failed allocating space for a double in column %d", i); + return startPos + numRows + finish_program_and_get_row_count(statement) + 1; + } + LOG_WINDOW("%d,%d is FLOAT %lf", startPos + numRows, i, value); + } else if (type == SQLITE_BLOB) { + // BLOB data + uint8_t const * blob = (uint8_t const *)sqlite3_column_blob(statement, i); + size_t size = sqlite3_column_bytes16(statement, i); + int offset = window->alloc(size); + if (!offset) { + window->freeLastRow(); + LOGE("Failed allocating %u bytes for blob at %d,%d", size, + startPos + numRows, i); + return startPos + numRows + finish_program_and_get_row_count(statement) + 1; + } + + window->copyIn(offset, blob, size); + + // This must be updated after the call to alloc(), since that + // may move the field around in the window + field_slot_t * fieldSlot = window->getFieldSlot(numRows, i); + fieldSlot->type = FIELD_TYPE_BLOB; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = size; + + LOG_WINDOW("%d,%d is Blob with %u bytes @ %d", startPos + numRows, i, size, offset); + } else if (type == SQLITE_NULL) { + // NULL field + window->putNull(numRows, i); + + LOG_WINDOW("%d,%d is NULL", startPos + numRows, i); + } else { + // Unknown data + LOGE("Unknown column type when filling database window"); + throw_sqlite3_exception(env, "Unknown column type when filling window"); + break; + } + } + + if (i < numColumns) { + // Not all the fields fit in the window + break; + } + + // Mark the row as complete in the window + numRows++; + } else if (err == SQLITE_DONE) { + // All rows processed, bail + LOG_WINDOW("Processed all rows"); + break; + } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) { + // The table is locked, retry + LOG_WINDOW("Database locked, retrying"); + if (retryCount > 50) { + LOGE("Bailing on database busy rety"); + break; + } + + // Sleep to give the thread holding the lock a chance to finish + usleep(1000); + + retryCount++; + continue; + } else { + throw_sqlite3_exception(env, GET_HANDLE(env, object)); + break; + } + } + + 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; +} + +static jint native_column_count(JNIEnv* env, jobject object) +{ + sqlite3_stmt * statement = GET_STATEMENT(env, object); + + return sqlite3_column_count(statement); +} + +static jstring native_column_name(JNIEnv* env, jobject object, jint columnIndex) +{ + sqlite3_stmt * statement = GET_STATEMENT(env, object); + char const * name; + + name = sqlite3_column_name(statement, columnIndex); + + return env->NewStringUTF(name); +} + + +static JNINativeMethod sMethods[] = +{ + /* name, signature, funcPtr */ + {"native_fill_window", "(Landroid/database/CursorWindow;II)I", (void *)native_fill_window}, + {"native_column_count", "()I", (void*)native_column_count}, + {"native_column_name", "(I)Ljava/lang/String;", (void *)native_column_name}, +}; + +int register_android_database_SQLiteQuery(JNIEnv * env) +{ + jclass clazz; + + clazz = env->FindClass("android/database/sqlite/SQLiteQuery"); + if (clazz == NULL) { + LOGE("Can't find android/database/sqlite/SQLiteQuery"); + return -1; + } + + gHandleField = env->GetFieldID(clazz, "nHandle", "I"); + gStatementField = env->GetFieldID(clazz, "nStatement", "I"); + + if (gHandleField == NULL || gStatementField == NULL) { + LOGE("Error locating fields"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + "android/database/sqlite/SQLiteQuery", sMethods, NELEM(sMethods)); +} + +} // namespace android diff --git a/core/jni/android_database_SQLiteStatement.cpp b/core/jni/android_database_SQLiteStatement.cpp new file mode 100644 index 0000000..b615895 --- /dev/null +++ b/core/jni/android_database_SQLiteStatement.cpp @@ -0,0 +1,149 @@ +/* //device/libs/android_runtime/android_database_SQLiteCursor.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#undef LOG_TAG +#define LOG_TAG "Cursor" + +#include <jni.h> +#include <JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> + +#include <sqlite3.h> + +#include <utils/Log.h> + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "sqlite3_exception.h" + +namespace android { + + +sqlite3_stmt * compile(JNIEnv* env, jobject object, + sqlite3 * handle, jstring sqlString); + +static jfieldID gHandleField; +static jfieldID gStatementField; + + +#define GET_STATEMENT(env, object) \ + (sqlite3_stmt *)env->GetIntField(object, gStatementField) +#define GET_HANDLE(env, object) \ + (sqlite3 *)env->GetIntField(object, gHandleField) + + +static void native_execute(JNIEnv* env, jobject object) +{ + int err; + sqlite3 * handle = GET_HANDLE(env, object); + sqlite3_stmt * statement = GET_STATEMENT(env, object); + + // Execute the statement + err = sqlite3_step(statement); + + // Throw an exception if an error occured + if (err != SQLITE_DONE) { + throw_sqlite3_exception_errcode(env, err, NULL); + } + + // Reset the statment so it's ready to use again + sqlite3_reset(statement); +} + +static jlong native_1x1_long(JNIEnv* env, jobject object) +{ + int err; + sqlite3 * handle = GET_HANDLE(env, object); + sqlite3_stmt * statement = GET_STATEMENT(env, object); + jlong value = -1; + + // Execute the statement + err = sqlite3_step(statement); + + // Handle the result + if (err == SQLITE_ROW) { + // No errors, read the data and return it + value = sqlite3_column_int64(statement, 0); + } else { + throw_sqlite3_exception_errcode(env, err, NULL); + } + + // Reset the statment so it's ready to use again + sqlite3_reset(statement); + + return value; +} + +static jstring native_1x1_string(JNIEnv* env, jobject object) +{ + int err; + sqlite3 * handle = GET_HANDLE(env, object); + sqlite3_stmt * statement = GET_STATEMENT(env, object); + jstring value = NULL; + + // Execute the statement + err = sqlite3_step(statement); + + // Handle the result + if (err == SQLITE_ROW) { + // No errors, read the data and return it + char const * text = (char const *)sqlite3_column_text(statement, 0); + value = env->NewStringUTF(text); + } else { + throw_sqlite3_exception_errcode(env, err, NULL); + } + + // Reset the statment so it's ready to use again + sqlite3_reset(statement); + + return value; +} + + +static JNINativeMethod sMethods[] = +{ + /* name, signature, funcPtr */ + {"native_execute", "()V", (void *)native_execute}, + {"native_1x1_long", "()J", (void *)native_1x1_long}, + {"native_1x1_string", "()Ljava/lang/String;", (void *)native_1x1_string}, +}; + +int register_android_database_SQLiteStatement(JNIEnv * env) +{ + jclass clazz; + + clazz = env->FindClass("android/database/sqlite/SQLiteStatement"); + if (clazz == NULL) { + LOGE("Can't find android/database/sqlite/SQLiteStatement"); + return -1; + } + + gHandleField = env->GetFieldID(clazz, "nHandle", "I"); + gStatementField = env->GetFieldID(clazz, "nStatement", "I"); + + if (gHandleField == NULL || gStatementField == NULL) { + LOGE("Error locating fields"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + "android/database/sqlite/SQLiteStatement", sMethods, NELEM(sMethods)); +} + +} // namespace android diff --git a/core/jni/android_ddm_DdmHandleNativeHeap.cpp b/core/jni/android_ddm_DdmHandleNativeHeap.cpp new file mode 100644 index 0000000..c3b4e3c --- /dev/null +++ b/core/jni/android_ddm_DdmHandleNativeHeap.cpp @@ -0,0 +1,144 @@ +/* //device/libs/android_runtime/android_ddm_DdmHandleNativeHeap.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#undef LOG_TAG +#define LOG_TAG "DdmHandleNativeHeap" + +#include <JNIHelp.h> +#include <jni.h> +#include <android_runtime/AndroidRuntime.h> + +#include <utils/Log.h> + +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +#if defined(__arm__) +extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, + size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); + +extern "C" void free_malloc_leak_info(uint8_t* info); +#endif + +#define MAPS_FILE_SIZE 65 * 1024 + +struct Header { + size_t mapSize; + size_t allocSize; + size_t allocInfoSize; + size_t totalMemory; + size_t backtraceSize; +}; + +namespace android { + +/* + * Retrieve the native heap information and the info from /proc/<self>/maps, + * copy them into a byte[] with a "struct Header" that holds data offsets, + * and return the array. + */ +static jbyteArray getLeakInfo(JNIEnv *env, jobject clazz) +{ +#if defined(__arm__) + // get the info in /proc/[pid]/map + Header header; + memset(&header, 0, sizeof(header)); + + pid_t pid = getpid(); + + char path[FILENAME_MAX]; + sprintf(path, "/proc/%d/maps", pid); + + struct stat sb; + int ret = stat(path, &sb); + + uint8_t* mapsFile = NULL; + if (ret == 0) { + mapsFile = (uint8_t*)malloc(MAPS_FILE_SIZE); + int fd = open(path, O_RDONLY); + + if (mapsFile != NULL && fd != -1) { + int amount = 0; + do { + uint8_t* ptr = mapsFile + header.mapSize; + amount = read(fd, ptr, MAPS_FILE_SIZE); + if (amount <= 0) { + if (errno != EINTR) + break; + else + continue; + } + header.mapSize += amount; + } while (header.mapSize < MAPS_FILE_SIZE); + + LOGD("**** read %d bytes from '%s'", (int) header.mapSize, path); + } + } + + uint8_t* allocBytes; + get_malloc_leak_info(&allocBytes, &header.allocSize, &header.allocInfoSize, + &header.totalMemory, &header.backtraceSize); + + jbyte* bytes = NULL; + jbyte* ptr = NULL; + jbyteArray array = env->NewByteArray(sizeof(Header) + header.mapSize + header.allocSize); + if (array == NULL) { + goto done; + } + + bytes = env->GetByteArrayElements(array, NULL); + ptr = bytes; + +// LOGD("*** mapSize: %d allocSize: %d allocInfoSize: %d totalMemory: %d", +// header.mapSize, header.allocSize, header.allocInfoSize, header.totalMemory); + + memcpy(ptr, &header, sizeof(header)); + ptr += sizeof(header); + + if (header.mapSize > 0 && mapsFile != NULL) { + memcpy(ptr, mapsFile, header.mapSize); + ptr += header.mapSize; + } + + memcpy(ptr, allocBytes, header.allocSize); + env->ReleaseByteArrayElements(array, bytes, 0); + +done: + if (mapsFile != NULL) { + free(mapsFile); + } + // free the info up! + free_malloc_leak_info(allocBytes); + + return array; +#else + return NULL; +#endif +} + +static JNINativeMethod method_table[] = { + { "getLeakInfo", "()[B", (void*)getLeakInfo }, +}; + +int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env) +{ + return AndroidRuntime::registerNativeMethods(env, "android/ddm/DdmHandleNativeHeap", method_table, NELEM(method_table)); +} + +}; diff --git a/core/jni/android_debug_JNITest.cpp b/core/jni/android_debug_JNITest.cpp new file mode 100644 index 0000000..f14201e --- /dev/null +++ b/core/jni/android_debug_JNITest.cpp @@ -0,0 +1,119 @@ +/* //device/libs/android_runtime/android_debug_JNITest.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "DebugJNI" + +#include "jni.h" +#include "nativehelper/JNIHelp.h" +#include "utils/Log.h" +#include "utils/misc.h" +//#include "android_runtime/AndroidRuntime.h" + +namespace android { + +/* + * Implements: + * native int part1(int intArg, double doubleArg, String stringArg, + * int[] arrayArg) + */ +static jint android_debug_JNITest_part1(JNIEnv* env, jobject object, + jint intArg, jdouble doubleArg, jstring stringArg, jobjectArray arrayArg) +{ + jclass clazz; + jmethodID part2id; + jsize arrayLen; + jint arrayVal; + int result = -2; + + LOGI("JNI test: in part1, intArg=%d, doubleArg=%.3f\n", intArg, doubleArg); + + /* find "int part2(double doubleArg, int fromArray, String stringArg)" */ + clazz = env->GetObjectClass(object); + part2id = env->GetMethodID(clazz, + "part2", "(DILjava/lang/String;)I"); + if (part2id == NULL) { + LOGE("JNI test: unable to find part2\n"); + return -1; + } + + /* get the length of the array */ + arrayLen = env->GetArrayLength(arrayArg); + LOGI(" array size is %d\n", arrayLen); + + /* + * Get the last element in the array. + * Use the Get<type>ArrayElements functions instead if you need access + * to multiple elements. + */ + arrayVal = (int) env->GetObjectArrayElement(arrayArg, arrayLen-1); + LOGI(" array val is %d\n", arrayVal); + + /* call this->part2 */ + result = env->CallIntMethod(object, part2id, + doubleArg, arrayVal, stringArg); + + return result; +} + +/* + * Implements: + * private static native int part3(String stringArg); + */ +static jint android_debug_JNITest_part3(JNIEnv* env, jclass clazz, + jstring stringArg) +{ + const char* utfChars; + jboolean isCopy; + + LOGI("JNI test: in part3\n"); + + utfChars = env->GetStringUTFChars(stringArg, &isCopy); + + LOGI(" String is '%s', isCopy=%d\n", (const char*) utfChars, isCopy); + + env->ReleaseStringUTFChars(stringArg, utfChars); + + return 2000; +} + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "part1", "(IDLjava/lang/String;[I)I", + (void*) android_debug_JNITest_part1 }, + { "part3", "(Ljava/lang/String;)I", + (void*) android_debug_JNITest_part3 }, +}; +int register_android_debug_JNITest(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "android/debug/JNITest", + gMethods, NELEM(gMethods)); +} + +#if 0 +/* trampoline into C++ */ +extern "C" +int register_android_debug_JNITest_C(JNIEnv* env) +{ + return android::register_android_debug_JNITest(env); +} +#endif + +}; // namespace android + diff --git a/core/jni/android_graphics_PixelFormat.cpp b/core/jni/android_graphics_PixelFormat.cpp new file mode 100644 index 0000000..0643622 --- /dev/null +++ b/core/jni/android_graphics_PixelFormat.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <assert.h> + +#include <ui/PixelFormat.h> + +#include "jni.h" +#include <android_runtime/AndroidRuntime.h> +#include <utils/misc.h> + +// ---------------------------------------------------------------------------- + +namespace android { + +// ---------------------------------------------------------------------------- + +struct offsets_t { + jfieldID bytesPerPixel; + jfieldID bitsPerPixel; +}; + +static offsets_t offsets; + +static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + jclass npeClazz = env->FindClass(exc); + env->ThrowNew(npeClazz, msg); +} + +// ---------------------------------------------------------------------------- + +static void android_graphics_getPixelFormatInfo( + JNIEnv* env, jobject clazz, jint format, jobject pixelFormatObject) +{ + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + if (err < 0) { + doThrow(env, "java/lang/IllegalArgumentException"); + return; + } + env->SetIntField(pixelFormatObject, offsets.bytesPerPixel, info.bytesPerPixel); + env->SetIntField(pixelFormatObject, offsets.bitsPerPixel, info.bitsPerPixel); +} +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/PixelFormat"; + +static void nativeClassInit(JNIEnv* env, jclass clazz); + +static JNINativeMethod gMethods[] = { + { "nativeClassInit", "()V", + (void*)nativeClassInit }, + { "getPixelFormatInfo", "(ILandroid/graphics/PixelFormat;)V", + (void*)android_graphics_getPixelFormatInfo + } +}; + +void nativeClassInit(JNIEnv* env, jclass clazz) +{ + offsets.bytesPerPixel = env->GetFieldID(clazz, "bytesPerPixel", "I"); + offsets.bitsPerPixel = env->GetFieldID(clazz, "bitsPerPixel", "I"); +} + +int register_android_graphics_PixelFormat(JNIEnv* env) +{ + return AndroidRuntime::registerNativeMethods(env, + kClassPathName, gMethods, NELEM(gMethods)); +} + +}; diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp new file mode 100644 index 0000000..a29c27b --- /dev/null +++ b/core/jni/android_hardware_Camera.cpp @@ -0,0 +1,451 @@ +/* +** +** 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 "Camera-JNI" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <ui/Surface.h> +#include <ui/Camera.h> +#include <utils/IMemory.h> + +using namespace android; + +enum CallbackMessageID { + kShutterCallback = 0, + kRawCallback = 1, + kJpegCallback = 2, + kPreviewCallback = 3, + kAutoFocusCallback = 4, + kErrorCallback = 5 +}; + +enum CameraError { + kCameraErrorUnknown = 1, + kCameraErrorMediaServer = 100 +}; + + +struct fields_t { + jfieldID context; + jfieldID surface; + jfieldID listener_context; + jmethodID post_event; +}; + +static fields_t fields; + +struct callback_cookie { + jclass camera_class; + jobject camera_ref; +}; + +static Camera *get_native_camera(JNIEnv *env, jobject thiz) +{ + Camera *c = reinterpret_cast<Camera*>(env->GetIntField(thiz, fields.context)); + if (c == 0) + jniThrowException(env, "java/lang/RuntimeException", "Method called after release()"); + + return c; +} + +static void err_callback(status_t err, void *cookie) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + callback_cookie *c = (callback_cookie *)cookie; + int error; + + switch (err) { + case DEAD_OBJECT: + error = kCameraErrorMediaServer; + break; + default: + error = kCameraErrorUnknown; + 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); +} + +// connect to camera service +static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) +{ + sp<Camera> c = Camera::connect(); + + if (c == NULL) { + jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); + return; + } + + // make sure camera hardware is alive + if (c->getStatus() != NO_ERROR) { + jniThrowException(env, "java/io/IOException", "Camera initialization failed"); + return; + } + + callback_cookie *cookie = new callback_cookie; + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + LOGE("Can't find android/hardware/Camera"); + // XXX no idea what to throw here, can this even happen? + jniThrowException(env, "java/lang/Exception", NULL); + return; + } + cookie->camera_class = (jclass)env->NewGlobalRef(clazz); + + // We use a weak reference so the Camera object can be garbage collected. + // The reference is only used as a proxy for callbacks. + cookie->camera_ref = env->NewGlobalRef(weak_this); + 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 + c->incStrong(thiz); +} + +// disconnect from camera service +static void android_hardware_Camera_release(JNIEnv *env, jobject thiz) +{ + 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) { + 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); + delete cookie; + env->SetIntField(thiz, fields.listener_context, 0); + } + } +} + +static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, jobject surface) +{ + Camera *c = get_native_camera(env, thiz); + if (c == 0) + return; + + sp<Surface> s = (Surface *)env->GetIntField(surface, fields.surface); + if (c->setPreviewDisplay(s) != NO_ERROR) { + jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed"); + return; + } +} + +static void preview_callback(const sp<IMemory>& mem, void *cookie) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + callback_cookie *c = (callback_cookie *)cookie; + int arg1 = 0, arg2 = 0; + jobject obj = NULL; + + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + + uint8_t *data = ((uint8_t *)heap->base()) + offset; + + jbyteArray array = env->NewByteArray(size); + if (array == NULL) { + LOGE("Couldn't allocate byte array for YUV data"); + return; + } + + jbyte *bytes = env->GetByteArrayElements(array, NULL); + memcpy(bytes, data, size); + env->ReleaseByteArrayElements(array, bytes, 0); + + obj = array; + + env->CallStaticVoidMethod(c->camera_class, fields.post_event, + c->camera_ref, kPreviewCallback, arg1, arg2, obj); + env->DeleteLocalRef(array); +} + +static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz) +{ + Camera *c = get_native_camera(env, thiz); + if (c == 0) + return; + + if (c->startPreview() != NO_ERROR) { + jniThrowException(env, "java/io/IOException", "startPreview failed"); + return; + } +} + +static void android_hardware_Camera_stopPreview(JNIEnv *env, jobject thiz) +{ + Camera *c = get_native_camera(env, thiz); + if (c == 0) + return; + + c->stopPreview(); +} + +static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed) +{ + Camera *c = get_native_camera(env, thiz); + if (c == 0) + return; + + // Important: Only install preview_callback if the Java code has called + // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy + // each preview frame for nothing. + callback_cookie *cookie = (callback_cookie *)env->GetIntField(thiz, fields.listener_context); + c->setFrameCallback(installed ? preview_callback : NULL, cookie); +} + +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, + success, 0, NULL); +} + + + +static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz) +{ + Camera *c = get_native_camera(env, thiz); + if (c == 0) + return; + callback_cookie *cookie = (callback_cookie *)env->GetIntField(thiz, fields.listener_context); + c->setAutoFocusCallback(autofocus_callback_impl, cookie); + if (c->autoFocus() != NO_ERROR) { + jniThrowException(env, "java/io/IOException", "autoFocus failed"); + } +} + +static void jpeg_callback(const sp<IMemory>& mem, void *cookie) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + callback_cookie *c = (callback_cookie *)cookie; + int arg1 = 0, arg2 = 0; + jobject obj = NULL; + + if (mem == NULL) { + env->CallStaticVoidMethod(c->camera_class, fields.post_event, + c->camera_ref, kJpegCallback, arg1, arg2, NULL); + return; + } + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + LOGV("jpeg_callback: mem off=%d, size=%d", offset, size); + + uint8_t *heap_base = (uint8_t *)heap->base(); + if (heap_base == NULL) { + LOGE("YUV heap is NULL"); + return; + } + + uint8_t *data = heap_base + offset; + + jbyteArray array = env->NewByteArray(size); + if (array == NULL) { + LOGE("Couldn't allocate byte array for JPEG data"); + return; + } + + jbyte *bytes = env->GetByteArrayElements(array, NULL); + memcpy(bytes, data, size); + env->ReleaseByteArrayElements(array, bytes, 0); + + obj = array; + + env->CallStaticVoidMethod(c->camera_class, fields.post_event, + c->camera_ref, kJpegCallback, arg1, arg2, obj); + env->DeleteLocalRef(array); +} + +static void shutter_callback_impl(void *cookie) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + callback_cookie *c = (callback_cookie *)cookie; + env->CallStaticVoidMethod(c->camera_class, fields.post_event, + c->camera_ref, kShutterCallback, 0, 0, NULL); +} + +static void raw_callback(const sp<IMemory>& mem __attribute__((unused)), + void *cookie) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + callback_cookie *c = (callback_cookie *)cookie; + env->CallStaticVoidMethod(c->camera_class, fields.post_event, + c->camera_ref, kRawCallback, 0, 0, NULL); +} + +static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz) +{ + Camera *c = get_native_camera(env, thiz); + if (c == 0) + return; + + callback_cookie *cookie = + (callback_cookie *)env->GetIntField(thiz, fields.listener_context); + c->setShutterCallback(shutter_callback_impl, cookie); + c->setRawCallback(raw_callback, cookie); + c->setJpegCallback(jpeg_callback, cookie); + if (c->takePicture() != NO_ERROR) { + jniThrowException(env, "java/io/IOException", "takePicture failed"); + return; + } + + return; +} + +static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params) +{ + Camera *c = get_native_camera(env, thiz); + if (c == 0) + return; + + const jchar* str = env->GetStringCritical(params, 0); + String8 params8; + if (params) { + params8 = String8(str, env->GetStringLength(params)); + env->ReleaseStringCritical(params, str); + } + if (c->setParameters(params8) != NO_ERROR) { + jniThrowException(env, "java/io/IllegalArgumentException", "setParameters failed"); + return; + } +} + +static jstring android_hardware_Camera_getParameters(JNIEnv *env, jobject thiz) +{ + Camera *c = get_native_camera(env, thiz); + if (c == 0) + return 0; + + return env->NewStringUTF(c->getParameters().string()); +} + +//------------------------------------------------- + +static JNINativeMethod camMethods[] = { + { "native_setup", + "(Ljava/lang/Object;)V", + (void*)android_hardware_Camera_native_setup }, + { "native_release", + "()V", + (void*)android_hardware_Camera_release }, + { "setPreviewDisplay", + "(Landroid/view/Surface;)V", + (void *)android_hardware_Camera_setPreviewDisplay }, + { "startPreview", + "()V", + (void *)android_hardware_Camera_startPreview }, + { "stopPreview", + "()V", + (void *)android_hardware_Camera_stopPreview }, + { "setHasPreviewCallback", + "(Z)V", + (void *)android_hardware_Camera_setHasPreviewCallback }, + { "native_autoFocus", + "()V", + (void *)android_hardware_Camera_autoFocus }, + { "native_takePicture", + "()V", + (void *)android_hardware_Camera_takePicture }, + { "native_setParameters", + "(Ljava/lang/String;)V", + (void *)android_hardware_Camera_setParameters }, + { "native_getParameters", + "()Ljava/lang/String;", + (void *)android_hardware_Camera_getParameters } +}; + +struct field { + const char *class_name; + const char *field_name; + const char *field_type; + jfieldID *jfield; +}; + +static int find_fields(JNIEnv *env, field *fields, int count) +{ + for (int i = 0; i < count; i++) { + field *f = &fields[i]; + jclass clazz = env->FindClass(f->class_name); + if (clazz == NULL) { + LOGE("Can't find %s", f->class_name); + return -1; + } + + jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type); + if (field == NULL) { + LOGE("Can't find %s.%s", f->class_name, f->field_name); + return -1; + } + + *(f->jfield) = field; + } + + return 0; +} + +// Get all the required offsets in java class and register native functions +int register_android_hardware_Camera(JNIEnv *env) +{ + field fields_to_find[] = { + { "android/hardware/Camera", "mNativeContext", "I", &fields.context }, + { "android/hardware/Camera", "mListenerContext", "I", &fields.listener_context }, + { "android/view/Surface", "mSurface", "I", &fields.surface } + }; + + if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0) + return -1; + + jclass clazz = env->FindClass("android/hardware/Camera"); + fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", + "(Ljava/lang/Object;IIILjava/lang/Object;)V"); + if (fields.post_event == NULL) { + LOGE("Can't find android/hardware/Camera.postEventFromNative"); + return -1; + } + + + // Register native functions + return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera", + camMethods, NELEM(camMethods)); +} + diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp new file mode 100644 index 0000000..e8dcd71 --- /dev/null +++ b/core/jni/android_hardware_SensorManager.cpp @@ -0,0 +1,82 @@ +/* + * 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 "Sensors" + +#include <hardware/sensors.h> + +#include "jni.h" +#include "JNIHelp.h" + + +namespace android { + +/* + * The method below are not thread-safe and not intended to be + */ + +static jint +android_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 +} + +static jint +android_data_close(JNIEnv *env, jclass clazz) +{ + return sensors_data_close(); +} + +static jint +android_data_poll(JNIEnv *env, jclass clazz, jfloatArray values, jint sensors) +{ + sensors_data_t data; + int res = sensors_data_poll(&data, sensors); + if (res) { + 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; + } + return res; +} + +static jint +android_data_get_sensors(JNIEnv *env, jclass clazz) +{ + return sensors_data_get_sensors(); +} + +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 }, +}; + +}; // namespace android + +using namespace android; + +int register_android_hardware_SensorManager(JNIEnv *env) +{ + return jniRegisterNativeMethods(env, "android/hardware/SensorManager", + gMethods, NELEM(gMethods)); +} diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp new file mode 100644 index 0000000..264cc51 --- /dev/null +++ b/core/jni/android_location_GpsLocationProvider.cpp @@ -0,0 +1,293 @@ +/* + * 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_TAG "GpsLocationProvider" + +#include "JNIHelp.h" +#include "jni.h" +#include "hardware/gps.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include <string.h> +#include <pthread.h> + + +static pthread_mutex_t sEventMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t sEventCond = PTHREAD_COND_INITIALIZER; +static jmethodID method_reportLocation; +static jmethodID method_reportStatus; +static jmethodID method_reportSvStatus; +static jmethodID method_xtraDownloadRequest; + +static const GpsInterface* sGpsInterface = NULL; +static const GpsXtraInterface* sGpsXtraInterface = NULL; + +// data written to by GPS callbacks +static GpsLocation sGpsLocation; +static GpsStatus sGpsStatus; +static GpsSvStatus sGpsSvStatus; + +// a copy of the data shared by android_location_GpsLocationProvider_wait_for_event +// and android_location_GpsLocationProvider_read_status +static GpsLocation sGpsLocationCopy; +static GpsStatus sGpsStatusCopy; +static GpsSvStatus sGpsSvStatusCopy; + +enum CallbackType { + kLocation = 1, + kStatus = 2, + kSvStatus = 4, + kXtraDownloadRequest = 8, + kDisableRequest = 16, +}; +static int sPendingCallbacks; + +namespace android { + +static void location_callback(GpsLocation* location) +{ + pthread_mutex_lock(&sEventMutex); + + sPendingCallbacks |= kLocation; + memcpy(&sGpsLocation, location, sizeof(sGpsLocation)); + + pthread_cond_signal(&sEventCond); + pthread_mutex_unlock(&sEventMutex); +} + +static void status_callback(GpsStatus* status) +{ + pthread_mutex_lock(&sEventMutex); + + sPendingCallbacks |= kStatus; + memcpy(&sGpsStatus, status, sizeof(sGpsStatus)); + + pthread_cond_signal(&sEventCond); + pthread_mutex_unlock(&sEventMutex); +} + +static void sv_status_callback(GpsSvStatus* sv_status) +{ + pthread_mutex_lock(&sEventMutex); + + sPendingCallbacks |= kSvStatus; + memcpy(&sGpsSvStatus, sv_status, sizeof(GpsSvStatus)); + + pthread_cond_signal(&sEventCond); + pthread_mutex_unlock(&sEventMutex); +} + +GpsCallbacks sGpsCallbacks = { + location_callback, + status_callback, + sv_status_callback, +}; + +static void +download_request_callback() +{ + pthread_mutex_lock(&sEventMutex); + sPendingCallbacks |= kXtraDownloadRequest; + pthread_cond_signal(&sEventCond); + pthread_mutex_unlock(&sEventMutex); +} + +GpsXtraCallbacks sGpsXtraCallbacks = { + download_request_callback, +}; + + +static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { + method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V"); + method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); + method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V"); + method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V"); +} + +static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) { + if (!sGpsInterface) + sGpsInterface = gps_get_interface(); + return (sGpsInterface != NULL); +} + +static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj) +{ + if (!sGpsInterface) + sGpsInterface = gps_get_interface(); + return (sGpsInterface && sGpsInterface->init(&sGpsCallbacks) == 0); +} + +static void android_location_GpsLocationProvider_disable(JNIEnv* env, jobject obj) +{ + pthread_mutex_lock(&sEventMutex); + sPendingCallbacks |= kDisableRequest; + pthread_cond_signal(&sEventCond); + pthread_mutex_unlock(&sEventMutex); +} + +static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject obj) +{ + sGpsInterface->cleanup(); +} + +static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj, jboolean singleFix, jint fixFrequency) +{ + int result = sGpsInterface->set_position_mode(GPS_POSITION_MODE_STANDALONE, (singleFix ? 0 : fixFrequency)); + if (result) { + return result; + } + + return (sGpsInterface->start() == 0); +} + +static jboolean android_location_GpsLocationProvider_stop(JNIEnv* env, jobject obj) +{ + return (sGpsInterface->stop() == 0); +} + +static void android_location_GpsLocationProvider_set_fix_frequency(JNIEnv* env, jobject obj, jint fixFrequency) +{ + if (sGpsInterface->set_fix_frequency) + sGpsInterface->set_fix_frequency(fixFrequency); +} + +static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* env, jobject obj, jint flags) +{ + sGpsInterface->delete_aiding_data(flags); +} + +static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, jobject obj) +{ + pthread_mutex_lock(&sEventMutex); + pthread_cond_wait(&sEventCond, &sEventMutex); + + // copy and clear the callback flags + int pendingCallbacks = sPendingCallbacks; + sPendingCallbacks = 0; + + // copy everything and unlock the mutex before calling into Java code to avoid the possibility + // of timeouts in the GPS engine. + memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy)); + memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy)); + memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy)); + pthread_mutex_unlock(&sEventMutex); + + if (pendingCallbacks & kLocation) { + env->CallVoidMethod(obj, method_reportLocation, sGpsLocationCopy.flags, + (jdouble)sGpsLocationCopy.latitude, (jdouble)sGpsLocationCopy.longitude, + (jdouble)sGpsLocationCopy.altitude, + (jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing, + (jfloat)sGpsLocationCopy.accuracy, (jlong)sGpsLocationCopy.timestamp); + } + if (pendingCallbacks & kStatus) { + env->CallVoidMethod(obj, method_reportStatus, sGpsStatusCopy.status); + } + if (pendingCallbacks & kSvStatus) { + env->CallVoidMethod(obj, method_reportSvStatus); + } + if (pendingCallbacks & kXtraDownloadRequest) { + env->CallVoidMethod(obj, method_xtraDownloadRequest); + } + if (pendingCallbacks & kDisableRequest) { + // don't need to do anything - we are just poking so wait_for_event will return. + } +} + +static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj, + jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray, + jintArray maskArray) +{ + // this should only be called from within a call to reportStatus, so we don't need to lock here + + jint* prns = env->GetIntArrayElements(prnArray, 0); + jfloat* snrs = env->GetFloatArrayElements(snrArray, 0); + jfloat* elev = env->GetFloatArrayElements(elevArray, 0); + jfloat* azim = env->GetFloatArrayElements(azumArray, 0); + jint* mask = env->GetIntArrayElements(maskArray, 0); + + int num_svs = sGpsSvStatusCopy.num_svs; + for (int i = 0; i < num_svs; i++) { + prns[i] = sGpsSvStatusCopy.sv_list[i].prn; + snrs[i] = sGpsSvStatusCopy.sv_list[i].snr; + elev[i] = sGpsSvStatusCopy.sv_list[i].elevation; + azim[i] = sGpsSvStatusCopy.sv_list[i].azimuth; + } + mask[0] = sGpsSvStatusCopy.ephemeris_mask; + mask[1] = sGpsSvStatusCopy.almanac_mask; + mask[2] = sGpsSvStatusCopy.used_in_fix_mask; + + env->ReleaseIntArrayElements(prnArray, prns, 0); + env->ReleaseFloatArrayElements(snrArray, snrs, 0); + env->ReleaseFloatArrayElements(elevArray, elev, 0); + env->ReleaseFloatArrayElements(azumArray, azim, 0); + env->ReleaseIntArrayElements(maskArray, mask, 0); + return num_svs; +} + +static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj, jlong time, + jlong timeReference, jint uncertainty) +{ + sGpsInterface->inject_time(time, timeReference, uncertainty); +} + +static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj) +{ + if (!sGpsXtraInterface) { + sGpsXtraInterface = (const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE); + if (sGpsXtraInterface) { + int result = sGpsXtraInterface->init(&sGpsXtraCallbacks); + if (result) { + sGpsXtraInterface = NULL; + } + } + } + + return (sGpsXtraInterface != NULL); +} + +static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj, + jbyteArray data, jint length) +{ + jbyte* bytes = env->GetByteArrayElements(data, 0); + sGpsXtraInterface->inject_xtra_data((char *)bytes, length); + env->ReleaseByteArrayElements(data, bytes, 0); +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native}, + {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported}, + {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init}, + {"native_disable", "()V", (void*)android_location_GpsLocationProvider_disable}, + {"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup}, + {"native_start", "(ZI)Z", (void*)android_location_GpsLocationProvider_start}, + {"native_stop", "()Z", (void*)android_location_GpsLocationProvider_stop}, + {"native_set_fix_frequency", "(I)V", (void*)android_location_GpsLocationProvider_set_fix_frequency}, + {"native_delete_aiding_data", "(I)V", (void*)android_location_GpsLocationProvider_delete_aiding_data}, + {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event}, + {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status}, + {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time}, + {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, + {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data}, +}; + +int register_android_location_GpsLocationProvider(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "com/android/internal/location/GpsLocationProvider", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp new file mode 100644 index 0000000..99785a2 --- /dev/null +++ b/core/jni/android_media_AudioSystem.cpp @@ -0,0 +1,186 @@ +/* //device/libs/android_runtime/android_media_AudioSystem.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "AudioSystem" +#include "utils/Log.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <math.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <media/AudioSystem.h> +#include <media/AudioTrack.h> + +// ---------------------------------------------------------------------------- + +using namespace android; + +enum AudioError { + kAudioStatusOk = 0, + kAudioStatusError = 1, + kAudioStatusMediaServerDied = 100 +}; + +static int check_AudioSystem_Command(status_t status) +{ + if (status == NO_ERROR) { + return kAudioStatusOk; + } else { + return kAudioStatusError; + } +} + +static int +android_media_AudioSystem_setVolume(JNIEnv *env, jobject clazz, jint type, jint volume) +{ + LOGV("setVolume(%d)", int(volume)); + if (int(type) == AudioTrack::VOICE_CALL) { + return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, float(volume) / 100.0)); + } else { + return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, AudioSystem::linearToLog(volume))); + } +} + +static int +android_media_AudioSystem_getVolume(JNIEnv *env, jobject clazz, jint type) +{ + float v; + int v_int = -1; + if (AudioSystem::getStreamVolume(int(type), &v) == NO_ERROR) { + // voice call volume is converted to log scale in the hardware + if (int(type) == AudioTrack::VOICE_CALL) { + v_int = lrint(v * 100.0); + } else { + v_int = AudioSystem::logToLinear(v); + } + } + return v_int; +} + +static int +android_media_AudioSystem_muteMicrophone(JNIEnv *env, jobject thiz, jboolean on) +{ + return check_AudioSystem_Command(AudioSystem::muteMicrophone(on)); +} + +static jboolean +android_media_AudioSystem_isMicrophoneMuted(JNIEnv *env, jobject thiz) +{ + bool state = false; + AudioSystem::isMicrophoneMuted(&state); + return state; +} + +static int +android_media_AudioSystem_setRouting(JNIEnv *env, jobject clazz, jint mode, jint routes, jint mask) +{ + return check_AudioSystem_Command(AudioSystem::setRouting(mode, uint32_t(routes), uint32_t(mask))); +} + +static jint +android_media_AudioSystem_getRouting(JNIEnv *env, jobject clazz, jint mode) +{ + uint32_t routes = -1; + AudioSystem::getRouting(mode, &routes); + return jint(routes); +} + +static int +android_media_AudioSystem_setMode(JNIEnv *env, jobject clazz, jint mode) +{ + return check_AudioSystem_Command(AudioSystem::setMode(mode)); +} + +static jint +android_media_AudioSystem_getMode(JNIEnv *env, jobject clazz) +{ + int mode = AudioSystem::MODE_INVALID; + AudioSystem::getMode(&mode); + return jint(mode); +} + +static jboolean +android_media_AudioSystem_isMusicActive(JNIEnv *env, jobject thiz) +{ + bool state = false; + AudioSystem::isMusicActive(&state); + return state; +} + +// Temporary interface, do not use +// TODO: Replace with a more generic key:value get/set mechanism +static void +android_media_AudioSystem_setParameter(JNIEnv *env, jobject thiz, jstring key, jstring value) +{ + const char *c_key = env->GetStringUTFChars(key, NULL); + const char *c_value = env->GetStringUTFChars(value, NULL); + AudioSystem::setParameter(c_key, c_value); + env->ReleaseStringUTFChars(key, c_key); + env->ReleaseStringUTFChars(value, c_value); +} + +void android_media_AudioSystem_error_callback(status_t err) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass clazz = env->FindClass("android/media/AudioSystem"); + + int error; + + switch (err) { + case DEAD_OBJECT: + error = kAudioStatusMediaServerDied; + break; + case NO_ERROR: + error = kAudioStatusOk; + break; + default: + error = kAudioStatusError; + break; + } + + env->CallStaticVoidMethod(clazz, env->GetStaticMethodID(clazz, "errorCallbackFromNative","(I)V"), error); +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gMethods[] = { + {"setVolume", "(II)I", (void *)android_media_AudioSystem_setVolume}, + {"getVolume", "(I)I", (void *)android_media_AudioSystem_getVolume}, + {"setParameter", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)android_media_AudioSystem_setParameter}, + {"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone}, + {"isMicrophoneMuted", "()Z", (void *)android_media_AudioSystem_isMicrophoneMuted}, + {"setRouting", "(III)I", (void *)android_media_AudioSystem_setRouting}, + {"getRouting", "(I)I", (void *)android_media_AudioSystem_getRouting}, + {"setMode", "(I)I", (void *)android_media_AudioSystem_setMode}, + {"getMode", "()I", (void *)android_media_AudioSystem_getMode}, + {"isMusicActive", "()Z", (void *)android_media_AudioSystem_isMusicActive}, +}; + +const char* const kClassPathName = "android/media/AudioSystem"; + +int register_android_media_AudioSystem(JNIEnv *env) +{ + AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback); + + return AndroidRuntime::registerNativeMethods(env, + "android/media/AudioSystem", gMethods, NELEM(gMethods)); +} diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp new file mode 100644 index 0000000..73c1283 --- /dev/null +++ b/core/jni/android_media_ToneGenerator.cpp @@ -0,0 +1,149 @@ +/* //device/libs/android_runtime/android_media_AudioSystem.cpp + ** + ** 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 "ToneGenerator" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include "utils/Log.h" +#include "media/AudioSystem.h" +#include "media/ToneGenerator.h" + +// ---------------------------------------------------------------------------- + +using namespace android; + +struct fields_t { + jfieldID context; +}; +static fields_t fields; + +static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, jint toneType) { + LOGV("android_media_ToneGenerator_startTone: %x\n", (int)thiz); + + ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz, + fields.context); + if (lpToneGen == NULL) { + jniThrowException(env, "java/lang/RuntimeException", "Method called after release()"); + return false; + } + + return lpToneGen->startTone(toneType); +} + +static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) { + LOGV("android_media_ToneGenerator_stopTone: %x\n", (int)thiz); + + ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz, + fields.context); + + LOGV("ToneGenerator lpToneGen: %x\n", (unsigned int)lpToneGen); + if (lpToneGen == NULL) { + jniThrowException(env, "java/lang/RuntimeException", "Method called after release()"); + return; + } + lpToneGen->stopTone(); +} + +static void android_media_ToneGenerator_release(JNIEnv *env, jobject thiz) { + ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz, + fields.context); + LOGV("android_media_ToneGenerator_release lpToneGen: %x\n", (int)lpToneGen); + + env->SetIntField(thiz, fields.context, 0); + + if (lpToneGen) { + delete lpToneGen; + } +} + +static void android_media_ToneGenerator_native_setup(JNIEnv *env, jobject thiz, + jint streamType, jint volume) { + ToneGenerator *lpToneGen = new ToneGenerator(streamType, AudioSystem::linearToLog(volume)); + + env->SetIntField(thiz, fields.context, 0); + + LOGV("android_media_ToneGenerator_native_setup jobject: %x\n", (int)thiz); + + if (lpToneGen == NULL) { + LOGE("ToneGenerator creation failed \n"); + jniThrowException(env, "java/lang/OutOfMemoryException", NULL); + return; + } + LOGV("ToneGenerator lpToneGen: %x\n", (unsigned int)lpToneGen); + + if (!lpToneGen->isInited()) { + LOGE("ToneGenerator init failed \n"); + jniThrowException(env, "java/lang/RuntimeException", "Init failed"); + return; + } + + // Stow our new C++ ToneGenerator in an opaque field in the Java object. + env->SetIntField(thiz, fields.context, (int)lpToneGen); + + LOGV("ToneGenerator fields.context: %x\n", env->GetIntField(thiz, fields.context)); +} + +static void android_media_ToneGenerator_native_finalize(JNIEnv *env, + jobject thiz) { + LOGV("android_media_ToneGenerator_native_finalize jobject: %x\n", (int)thiz); + + ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz, + fields.context); + + if (lpToneGen) { + LOGV("delete lpToneGen: %x\n", (int)lpToneGen); + delete lpToneGen; + } +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gMethods[] = { + { "startTone", "(I)Z", (void *)android_media_ToneGenerator_startTone }, + { "stopTone", "()V", (void *)android_media_ToneGenerator_stopTone }, + { "release", "()V", (void *)android_media_ToneGenerator_release }, + { "native_setup", "(II)V", (void *)android_media_ToneGenerator_native_setup }, + { "native_finalize", "()V", (void *)android_media_ToneGenerator_native_finalize } +}; + + +int register_android_media_ToneGenerator(JNIEnv *env) { + jclass clazz; + + clazz = env->FindClass("android/media/ToneGenerator"); + if (clazz == NULL) { + LOGE("Can't find %s", "android/media/ToneGenerator"); + return -1; + } + + fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (fields.context == NULL) { + LOGE("Can't find ToneGenerator.mNativeContext"); + return -1; + } + LOGV("register_android_media_ToneGenerator ToneGenerator fields.context: %x", (unsigned int)fields.context); + + return AndroidRuntime::registerNativeMethods(env, + "android/media/ToneGenerator", gMethods, NELEM(gMethods)); +} diff --git a/core/jni/android_message_digest_sha1.cpp b/core/jni/android_message_digest_sha1.cpp new file mode 100644 index 0000000..480bbf8 --- /dev/null +++ b/core/jni/android_message_digest_sha1.cpp @@ -0,0 +1,146 @@ +/* //device/libs/android_runtime/android_message_digest_sha1.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "jni.h" +#include <JNIHelp.h> +#include "android_runtime/AndroidRuntime.h" + +#include <openssl/sha.h> + +//#define _DEBUG 1 + +// ---------------------------------------------------------------------------- + +using namespace android; + +// ---------------------------------------------------------------------------- + +struct fields_t { + jfieldID context; +}; +static fields_t fields; + +static void native_init(JNIEnv *env, jobject clazz) +{ + SHA_CTX* context; + +#ifdef _DEBUG + printf("sha1.native_init\n"); +#endif + + context = (SHA_CTX *)malloc(sizeof(SHA_CTX)); + SHA1_Init(context); + + env->SetIntField(clazz, fields.context, (int)context); +} + +static void native_reset(JNIEnv *env, jobject clazz) +{ + SHA_CTX *context = (SHA_CTX *)env->GetIntField(clazz, fields.context); + if (context != NULL) { +#ifdef _DEBUG + printf("sha1.native_reset: free context\n"); +#endif + free(context); + env->SetIntField(clazz, fields.context, 0 ); + } +} + + +static void native_update(JNIEnv *env, jobject clazz, jbyteArray dataArray) +{ +#ifdef _DEBUG + printf("sha1.native_update\n"); +#endif + jbyte * data; + jsize dataSize; + SHA_CTX *context = (SHA_CTX *)env->GetIntField(clazz, fields.context); + + if (context == NULL) { +#ifdef _DEBUG + printf("sha1.native_update: context is NULL, call init...\n"); +#endif + native_init(env, clazz); + context = (SHA_CTX *)env->GetIntField(clazz, fields.context); + } + + data = env->GetByteArrayElements(dataArray, NULL); + if (data == NULL) { + LOGE("Unable to get byte array elements"); + jniThrowException(env, "java/lang/IllegalArgumentException", + "Invalid data array when calling MessageDigest.update()"); + return; + } + dataSize = env->GetArrayLength(dataArray); + + SHA1_Update(context, data, dataSize); + + env->ReleaseByteArrayElements(dataArray, data, 0); +} + +static jbyteArray native_digest(JNIEnv *env, jobject clazz) +{ +#ifdef _DEBUG + printf("sha1.native_digest\n"); +#endif + jbyteArray array; + jbyte md[SHA_DIGEST_LENGTH]; + SHA_CTX *context = (SHA_CTX *)env->GetIntField(clazz, fields.context); + + SHA1_Final((uint8_t*)md, context); + + array = env->NewByteArray(SHA_DIGEST_LENGTH); + LOG_ASSERT(array, "Native could not create new byte[]"); + + env->SetByteArrayRegion(array, 0, SHA_DIGEST_LENGTH, md); + + native_reset(env, clazz); + + return array; +} + + +static JNINativeMethod method_table[] = +{ + /* name, signature, funcPtr */ + {"init", "()V", (void *)native_init}, + {"update", "([B)V", (void *)native_update}, + {"digest", "()[B", (void *)native_digest}, + {"reset", "()V", (void *)native_reset}, +}; + +int register_android_message_digest_sha1(JNIEnv *env) +{ + jclass clazz; + + clazz = env->FindClass("android/security/Sha1MessageDigest"); + if (clazz == NULL) { + LOGE("Can't find android/security/Sha1MessageDigest"); + return -1; + } + + fields.context = env->GetFieldID(clazz, "mNativeSha1Context", "I"); + if (fields.context == NULL) { + LOGE("Can't find Sha1MessageDigest.mNativeSha1Context"); + return -1; + } + + return AndroidRuntime::registerNativeMethods( + env, "android/security/Sha1MessageDigest", + method_table, NELEM(method_table)); +} + diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp new file mode 100644 index 0000000..abd0961 --- /dev/null +++ b/core/jni/android_net_LocalSocketImpl.cpp @@ -0,0 +1,966 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "LocalSocketImpl" + +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/ioctl.h> + +#include <cutils/sockets.h> +#include <netinet/tcp.h> +#include <cutils/properties.h> +#include <cutils/adb_networking.h> + +namespace android { + +static jfieldID field_inboundFileDescriptors; +static jfieldID field_outboundFileDescriptors; +static jclass class_Credentials; +static jclass class_FileDescriptor; +static jmethodID method_CredentialsInit; + +/* + * private native FileDescriptor + * create_native(boolean stream) + * throws IOException; + */ +static jobject +socket_create (JNIEnv *env, jobject object, jboolean stream) +{ + int ret; + + ret = socket(PF_LOCAL, stream ? SOCK_STREAM : SOCK_DGRAM, 0); + + if (ret < 0) { + jniThrowIOException(env, errno); + return NULL; + } + + return jniCreateFileDescriptor(env,ret); +} + +/* private native void connectLocal(FileDescriptor fd, + * String name, int namespace) throws IOException + */ +static void +socket_connect_local(JNIEnv *env, jobject object, + jobject fileDescriptor, jstring name, jint namespaceId) +{ + int ret; + const char *nameUtf8; + int fd; + + nameUtf8 = env->GetStringUTFChars(name, NULL); + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + ret = socket_local_client_connect( + fd, + nameUtf8, + namespaceId, + SOCK_STREAM); + + env->ReleaseStringUTFChars(name, nameUtf8); + + if (ret < 0) { + jniThrowIOException(env, errno); + return; + } +} + +#define DEFAULT_BACKLOG 4 + +/* private native void bindLocal(FileDescriptor fd, String name, namespace) + * throws IOException; + */ + +static void +socket_bind_local (JNIEnv *env, jobject object, jobject fileDescriptor, + jstring name, jint namespaceId) +{ + int ret; + int fd; + const char *nameUtf8; + + + if (name == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + } + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + nameUtf8 = env->GetStringUTFChars(name, NULL); + + ret = socket_local_server_bind(fd, nameUtf8, namespaceId); + + env->ReleaseStringUTFChars(name, nameUtf8); + + if (ret < 0) { + jniThrowIOException(env, errno); + return; + } +} + +/* private native void listen_native(int fd, int backlog) throws IOException; */ +static void +socket_listen (JNIEnv *env, jobject object, jobject fileDescriptor, int backlog) +{ + int ret; + int fd; + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + ret = listen(fd, backlog); + + if (ret < 0) { + jniThrowIOException(env, errno); + return; + } +} + +/* private native FileDescriptor +** accept (FileDescriptor fd, LocalSocketImpl s) +** throws IOException; +*/ +static jobject +socket_accept (JNIEnv *env, jobject object, jobject fileDescriptor, jobject s) +{ + union { + struct sockaddr address; + struct sockaddr_un un_address; + } sa; + + int ret; + int retFD; + int fd; + socklen_t addrlen; + + if (s == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return NULL; + } + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return NULL; + } + + do { + addrlen = sizeof(sa); + ret = accept(fd, &(sa.address), &addrlen); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + jniThrowIOException(env, errno); + return NULL; + } + + retFD = ret; + + return jniCreateFileDescriptor(env, retFD); +} + +/* private native void shutdown(FileDescriptor fd, boolean shutdownInput) */ + +static void +socket_shutdown (JNIEnv *env, jobject object, jobject fileDescriptor, + jboolean shutdownInput) +{ + int ret; + int fd; + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + ret = shutdown(fd, shutdownInput ? SHUT_RD : SHUT_WR); + + if (ret < 0) { + jniThrowIOException(env, errno); + return; + } +} + +static bool +java_opt_to_real(int optID, int* opt, int* level) +{ + switch (optID) + { + case 4098: + *opt = SO_RCVBUF; + *level = SOL_SOCKET; + return true; + case 4097: + *opt = SO_SNDBUF; + *level = SOL_SOCKET; + return true; + case 4102: + *opt = SO_SNDTIMEO; + *level = SOL_SOCKET; + return true; + case 128: + *opt = SO_LINGER; + *level = SOL_SOCKET; + return true; + case 1: + *opt = TCP_NODELAY; + *level = IPPROTO_TCP; + return true; + case 4: + *opt = SO_REUSEADDR; + *level = SOL_SOCKET; + return true; + + } + return false; +} + +static jint +socket_getOption(JNIEnv *env, jobject object, jobject fileDescriptor, int optID) +{ + int ret, value; + int opt, level; + int fd; + + socklen_t size = sizeof(int); + + if (!java_opt_to_real(optID, &opt, &level)) { + jniThrowIOException(env, -1); + return 0; + } + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return 0; + } + + switch (opt) + { + case SO_LINGER: + { + struct linger lingr; + size = sizeof(lingr); + ret = getsockopt(fd, level, opt, &lingr, &size); + if (!lingr.l_onoff) { + value = -1; + } else { + value = lingr.l_linger; + } + break; + } + default: + ret = getsockopt(fd, level, opt, &value, &size); + break; + } + + + if (ret != 0) { + jniThrowIOException(env, errno); + return 0; + } + + return value; +} + +static void socket_setOption( + JNIEnv *env, jobject object, jobject fileDescriptor, int optID, + jint boolValue, jint intValue) { + int ret; + int optname; + int level; + int fd; + + if (!java_opt_to_real(optID, &optname, &level)) { + jniThrowIOException(env, -1); + return; + } + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + switch (optname) { + case SO_LINGER: { + /* + * SO_LINGER is special because it needs to use a special + * "linger" struct as well as use the incoming boolean + * argument specially. + */ + struct linger lingr; + lingr.l_onoff = boolValue ? 1 : 0; // Force it to be 0 or 1. + lingr.l_linger = intValue; + ret = setsockopt(fd, level, optname, &lingr, sizeof(lingr)); + break; + } + case SO_SNDTIMEO: { + /* + * SO_TIMEOUT from the core library gets converted to + * SO_SNDTIMEO, but the option is supposed to set both + * send and receive timeouts. Note: The incoming timeout + * value is in milliseconds. + */ + struct timeval timeout; + timeout.tv_sec = intValue / 1000; + timeout.tv_usec = (intValue % 1000) * 1000; + + ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, + (void *)&timeout, sizeof(timeout)); + + if (ret == 0) { + ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, + (void *)&timeout, sizeof(timeout)); + } + + break; + } + default: { + /* + * In all other cases, the translated option level and + * optname may be used directly for a call to setsockopt(). + */ + ret = setsockopt(fd, level, optname, &intValue, sizeof(intValue)); + break; + } + } + + if (ret != 0) { + jniThrowIOException(env, errno); + return; + } +} + +static jint socket_available (JNIEnv *env, jobject object, + jobject fileDescriptor) +{ + int fd; + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return (jint)-1; + } + +#if 1 + int avail; + int ret = ioctl(fd, FIONREAD, &avail); + + // If this were a non-socket fd, there would be other cases to worry + // about... + + if (ret < 0) { + jniThrowIOException(env, errno); + return (jint) 0; + } + + return (jint)avail; +#else +// there appears to be a bionic bug that prevents this version from working. + + ssize_t ret; + struct msghdr msg; + + memset(&msg, 0, sizeof(msg)); + + do { + ret = recvmsg(fd, &msg, MSG_PEEK | MSG_DONTWAIT | MSG_NOSIGNAL); + } while (ret < 0 && errno == EINTR); + + + // MSG_PEEK returns 0 on EOF and EWOULDBLOCK on none available + if (ret < 0 && errno == EWOULDBLOCK) { + return 0; + } if (ret < 0) { + jniThrowIOException(env, errno); + return -1; + } + + return (jint)ret; +#endif +} + +static void socket_close (JNIEnv *env, jobject object, jobject fileDescriptor) +{ + int fd; + int err; + + if (fileDescriptor == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + do { + err = close(fd); + } while (err < 0 && errno == EINTR); + + if (err < 0) { + jniThrowIOException(env, errno); + return; + } +} + +/** + * Processes ancillary data, handling only + * SCM_RIGHTS. Creates appropriate objects and sets appropriate + * fields in the LocalSocketImpl object. Returns 0 on success + * or -1 if an exception was thrown. + */ +static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg) +{ + struct cmsghdr *cmsgptr; + + for (cmsgptr = CMSG_FIRSTHDR(pMsg); + cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) { + + if (cmsgptr->cmsg_level != SOL_SOCKET) { + continue; + } + + if (cmsgptr->cmsg_type == SCM_RIGHTS) { + int *pDescriptors = (int *)CMSG_DATA(cmsgptr); + jobjectArray fdArray; + int count + = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int)); + + if (count < 0) { + jniThrowException(env, "java/io/IOException", + "invalid cmsg length"); + } + + fdArray = env->NewObjectArray(count, class_FileDescriptor, NULL); + + if (fdArray == NULL) { + return -1; + } + + for (int i = 0; i < count; i++) { + jobject fdObject + = jniCreateFileDescriptor(env, pDescriptors[i]); + + if (env->ExceptionOccurred() != NULL) { + return -1; + } + + env->SetObjectArrayElement(fdArray, i, fdObject); + + if (env->ExceptionOccurred() != NULL) { + return -1; + } + } + + env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray); + + if (env->ExceptionOccurred() != NULL) { + return -1; + } + } + } + + return 0; +} + +/** + * Reads data from a socket into buf, processing any ancillary data + * and adding it to thisJ. + * + * Returns the length of normal data read, or -1 if an exception has + * been thrown in this function. + */ +static ssize_t socket_read_all(JNIEnv *env, jobject thisJ, int fd, + void *buffer, size_t len) +{ + ssize_t ret; + ssize_t bytesread = 0; + struct msghdr msg; + struct iovec iv; + unsigned char *buf = (unsigned char *)buffer; + // Enough buffer for a pile of fd's. We throw an exception if + // this buffer is too small. + struct cmsghdr cmsgbuf[2*sizeof(cmsghdr) + 0x100]; + + memset(&msg, 0, sizeof(msg)); + memset(&iv, 0, sizeof(iv)); + + iv.iov_base = buf; + iv.iov_len = len; + + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + do { + ret = recvmsg(fd, &msg, MSG_NOSIGNAL); + } while (ret < 0 && errno == EINTR); + + if (ret < 0 && errno == EPIPE) { + // Treat this as an end of stream + return 0; + } + + if (ret < 0) { + jniThrowIOException(env, errno); + return -1; + } + + if ((msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) { + // To us, any of the above flags are a fatal error + + jniThrowException(env, "java/io/IOException", + "Unexpected error or truncation during recvmsg()"); + + return -1; + } + + if (ret >= 0) { + socket_process_cmsg(env, thisJ, &msg); + } + + return ret; +} + +/** + * Writes all the data in the specified buffer to the specified socket. + * + * Returns 0 on success or -1 if an exception was thrown. + */ +static int socket_write_all(JNIEnv *env, jobject object, int fd, + void *buf, size_t len) +{ + ssize_t ret; + struct msghdr msg; + unsigned char *buffer = (unsigned char *)buf; + memset(&msg, 0, sizeof(msg)); + + jobjectArray outboundFds + = (jobjectArray)env->GetObjectField( + object, field_outboundFileDescriptors); + + if (env->ExceptionOccurred() != NULL) { + return -1; + } + + struct cmsghdr *cmsg; + int countFds = outboundFds == NULL ? 0 : env->GetArrayLength(outboundFds); + int fds[countFds]; + char msgbuf[CMSG_SPACE(countFds)]; + + // Add any pending outbound file descriptors to the message + if (outboundFds != NULL) { + + if (env->ExceptionOccurred() != NULL) { + return -1; + } + + for (int i = 0; i < countFds; i++) { + jobject fdObject = env->GetObjectArrayElement(outboundFds, i); + if (env->ExceptionOccurred() != NULL) { + return -1; + } + + fds[i] = jniGetFDFromFileDescriptor(env, fdObject); + if (env->ExceptionOccurred() != NULL) { + return -1; + } + } + + // See "man cmsg" really + msg.msg_control = msgbuf; + msg.msg_controllen = sizeof msgbuf; + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof fds); + memcpy(CMSG_DATA(cmsg), fds, sizeof fds); + } + + // We only write our msg_control during the first write + while (len > 0) { + struct iovec iv; + memset(&iv, 0, sizeof(iv)); + + iv.iov_base = buffer; + iv.iov_len = len; + + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + + do { + ret = sendmsg(fd, &msg, MSG_NOSIGNAL); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + jniThrowIOException(env, errno); + return -1; + } + + buffer += ret; + len -= ret; + + // Wipes out any msg_control too + memset(&msg, 0, sizeof(msg)); + } + + return 0; +} + +static jint socket_read (JNIEnv *env, jobject object, jobject fileDescriptor) +{ + int fd; + int err; + + if (fileDescriptor == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return (jint)-1; + } + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return (jint)0; + } + + unsigned char buf; + + err = socket_read_all(env, object, fd, &buf, 1); + + if (err < 0) { + jniThrowIOException(env, errno); + return (jint)0; + } + + if (err == 0) { + // end of file + return (jint)-1; + } + + return (jint)buf; +} + +static jint socket_readba (JNIEnv *env, jobject object, + jbyteArray buffer, jint off, jint len, jobject fileDescriptor) +{ + int fd; + jbyte* byteBuffer; + int ret; + + if (fileDescriptor == NULL || buffer == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return (jint)-1; + } + + if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) { + jniThrowException(env, "java/lang/ArrayIndexOutOfBounds", NULL); + return (jint)-1; + } + + if (len == 0) { + // because socket_read_all returns 0 on EOF + return 0; + } + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return (jint)-1; + } + + byteBuffer = env->GetByteArrayElements(buffer, NULL); + + if (NULL == byteBuffer) { + // an exception will have been thrown + return (jint)-1; + } + + ret = socket_read_all(env, object, + fd, byteBuffer + off, len); + + // A return of -1 above means an exception is pending + + env->ReleaseByteArrayElements(buffer, byteBuffer, 0); + + return (jint) ((ret == 0) ? -1 : ret); +} + +static void socket_write (JNIEnv *env, jobject object, + jint b, jobject fileDescriptor) +{ + int fd; + int err; + + if (fileDescriptor == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + err = socket_write_all(env, object, fd, &b, 1); + + // A return of -1 above means an exception is pending +} + +static void socket_writeba (JNIEnv *env, jobject object, + jbyteArray buffer, jint off, jint len, jobject fileDescriptor) +{ + int fd; + int err; + jbyte* byteBuffer; + + if (fileDescriptor == NULL || buffer == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) { + jniThrowException(env, "java/lang/ArrayIndexOutOfBounds", NULL); + return; + } + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + byteBuffer = env->GetByteArrayElements(buffer,NULL); + + if (NULL == byteBuffer) { + // an exception will have been thrown + return; + } + + err = socket_write_all(env, object, fd, + byteBuffer + off, len); + + // A return of -1 above means an exception is pending + + env->ReleaseByteArrayElements(buffer, byteBuffer, JNI_ABORT); +} + +static jobject socket_get_peer_credentials(JNIEnv *env, + jobject object, jobject fileDescriptor) +{ + int err; + int fd; + + if (fileDescriptor == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return NULL; + } + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return NULL; + } + + struct ucred creds; + + memset(&creds, 0, sizeof(creds)); + socklen_t szCreds = sizeof(creds); + + err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds); + + if (err < 0) { + jniThrowIOException(env, errno); + return NULL; + } + + if (szCreds == 0) { + return NULL; + } + + return env->NewObject(class_Credentials, method_CredentialsInit, + creds.pid, creds.uid, creds.gid); +} + +#if 0 +//TODO change this to return an instance of LocalSocketAddress +static jobject socket_getSockName(JNIEnv *env, + jobject object, jobject fileDescriptor) +{ + int err; + int fd; + + if (fileDescriptor == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return NULL; + } + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return NULL; + } + + union { + struct sockaddr address; + struct sockaddr_un un_address; + } sa; + + memset(&sa, 0, sizeof(sa)); + + socklen_t namelen = sizeof(sa); + err = getsockname(fd, &(sa.address), &namelen); + + if (err < 0) { + jniThrowIOException(env, errno); + return NULL; + } + + if (sa.address.sa_family != AF_UNIX) { + // We think we're an impl only for AF_UNIX, so this should never happen. + + jniThrowIOException(env, EINVAL); + return NULL; + } + + if (sa.un_address.sun_path[0] == '\0') { + } else { + } + + + + +} +#endif + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + {"getOption_native", "(Ljava/io/FileDescriptor;I)I", (void*)socket_getOption}, + {"setOption_native", "(Ljava/io/FileDescriptor;III)V", (void*)socket_setOption}, + {"create_native", "(Z)Ljava/io/FileDescriptor;", (void*)socket_create}, + {"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", + (void*)socket_connect_local}, + {"bindLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", (void*)socket_bind_local}, + {"listen_native", "(Ljava/io/FileDescriptor;I)V", (void*)socket_listen}, + {"accept", "(Ljava/io/FileDescriptor;Landroid/net/LocalSocketImpl;)Ljava/io/FileDescriptor;", (void*)socket_accept}, + {"shutdown", "(Ljava/io/FileDescriptor;Z)V", (void*)socket_shutdown}, + {"available_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_available}, + {"close_native", "(Ljava/io/FileDescriptor;)V", (void*) socket_close}, + {"read_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_read}, + {"readba_native", "([BIILjava/io/FileDescriptor;)I", (void*) socket_readba}, + {"writeba_native", "([BIILjava/io/FileDescriptor;)V", (void*) socket_writeba}, + {"write_native", "(ILjava/io/FileDescriptor;)V", (void*) socket_write}, + {"getPeerCredentials_native", + "(Ljava/io/FileDescriptor;)Landroid/net/Credentials;", + (void*) socket_get_peer_credentials} + //,{"getSockName_native", "(Ljava/io/FileDescriptor;)Ljava/lang/String;", + // (void *) socket_getSockName} + +}; + +int register_android_net_LocalSocketImpl(JNIEnv *env) +{ + jclass clazz; + + clazz = env->FindClass("android/net/LocalSocketImpl"); + + if (clazz == NULL) { + goto error; + } + + field_inboundFileDescriptors = env->GetFieldID(clazz, + "inboundFileDescriptors", "[Ljava/io/FileDescriptor;"); + + if (field_inboundFileDescriptors == NULL) { + goto error; + } + + field_outboundFileDescriptors = env->GetFieldID(clazz, + "outboundFileDescriptors", "[Ljava/io/FileDescriptor;"); + + if (field_outboundFileDescriptors == NULL) { + goto error; + } + + class_Credentials = env->FindClass("android/net/Credentials"); + + if (class_Credentials == NULL) { + goto error; + } + + class_Credentials = (jclass)env->NewGlobalRef(class_Credentials); + + class_FileDescriptor = env->FindClass("java/io/FileDescriptor"); + + if (class_FileDescriptor == NULL) { + goto error; + } + + class_FileDescriptor = (jclass)env->NewGlobalRef(class_FileDescriptor); + + method_CredentialsInit + = env->GetMethodID(class_Credentials, "<init>", "(III)V"); + + if (method_CredentialsInit == NULL) { + goto error; + } + + return jniRegisterNativeMethods(env, + "android/net/LocalSocketImpl", gMethods, NELEM(gMethods)); + +error: + LOGE("Error registering android.net.LocalSocketImpl"); + return -1; +} + +}; diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp new file mode 100644 index 0000000..417ce54 --- /dev/null +++ b/core/jni/android_net_NetUtils.cpp @@ -0,0 +1,235 @@ +/* + * 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 "NetUtils" + +#include "jni.h" +#include <utils/misc.h> +#include <android_runtime/AndroidRuntime.h> +#include <utils/Log.h> +#include <arpa/inet.h> + +extern "C" { +int ifc_disable(const char *ifname); +int ifc_add_host_route(const char *ifname, uint32_t addr); +int ifc_remove_host_routes(const char *ifname); +int ifc_set_default_route(const char *ifname, uint32_t gateway); +int ifc_get_default_route(const char *ifname); +int ifc_remove_default_route(const char *ifname); +int ifc_reset_connections(const char *ifname); +int ifc_configure(const char *ifname, in_addr_t ipaddr, in_addr_t netmask, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2); + +int dhcp_do_request(const char *ifname, + in_addr_t *ipaddr, + in_addr_t *gateway, + in_addr_t *mask, + in_addr_t *dns1, + in_addr_t *dns2, + in_addr_t *server, + uint32_t *lease); +int dhcp_stop(const char *ifname); +char *dhcp_get_errmsg(); +} + +#define NETUTILS_PKG_NAME "android/net/NetworkUtils" + +namespace android { + +/* + * The following remembers the jfieldID's of the fields + * of the DhcpInfo Java object, so that we don't have + * to look them up every time. + */ +static struct fieldIds { + jclass dhcpInfoClass; + jmethodID constructorId; + jfieldID ipaddress; + jfieldID gateway; + jfieldID netmask; + jfieldID dns1; + jfieldID dns2; + jfieldID serverAddress; + jfieldID leaseDuration; +} dhcpInfoFieldIds; + +static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstring ifname) +{ + int result; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::ifc_disable(nameStr); + env->ReleaseStringUTFChars(ifname, nameStr); + return (jint)result; +} + +static jint android_net_utils_addHostRoute(JNIEnv* env, jobject clazz, jstring ifname, jint addr) +{ + int result; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::ifc_add_host_route(nameStr, addr); + env->ReleaseStringUTFChars(ifname, nameStr); + return (jint)result; +} + +static jint android_net_utils_removeHostRoutes(JNIEnv* env, jobject clazz, jstring ifname) +{ + int result; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::ifc_remove_host_routes(nameStr); + env->ReleaseStringUTFChars(ifname, nameStr); + return (jint)result; +} + +static jint android_net_utils_setDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname, jint gateway) +{ + int result; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::ifc_set_default_route(nameStr, gateway); + env->ReleaseStringUTFChars(ifname, nameStr); + return (jint)result; +} + +static jint android_net_utils_getDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname) +{ + int result; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::ifc_get_default_route(nameStr); + env->ReleaseStringUTFChars(ifname, nameStr); + return (jint)result; +} + +static jint android_net_utils_removeDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname) +{ + int result; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::ifc_remove_default_route(nameStr); + env->ReleaseStringUTFChars(ifname, nameStr); + return (jint)result; +} + +static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, jstring ifname) +{ + int result; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::ifc_reset_connections(nameStr); + env->ReleaseStringUTFChars(ifname, nameStr); + return (jint)result; +} + +static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info) +{ + int result; + in_addr_t ipaddr, gateway, mask, dns1, dns2, server; + uint32_t lease; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::dhcp_do_request(nameStr, &ipaddr, &gateway, &mask, + &dns1, &dns2, &server, &lease); + env->ReleaseStringUTFChars(ifname, nameStr); + if (result == 0 && dhcpInfoFieldIds.dhcpInfoClass != NULL) { + env->SetIntField(info, dhcpInfoFieldIds.ipaddress, ipaddr); + env->SetIntField(info, dhcpInfoFieldIds.gateway, gateway); + env->SetIntField(info, dhcpInfoFieldIds.netmask, mask); + env->SetIntField(info, dhcpInfoFieldIds.dns1, dns1); + env->SetIntField(info, dhcpInfoFieldIds.dns2, dns2); + env->SetIntField(info, dhcpInfoFieldIds.serverAddress, server); + env->SetIntField(info, dhcpInfoFieldIds.leaseDuration, lease); + } + return (jboolean)(result == 0); +} + +static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info) +{ + int result; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::dhcp_stop(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()); +} + +static jboolean android_net_utils_configureInterface(JNIEnv* env, + jobject clazz, + jstring ifname, + jint ipaddr, + jint mask, + jint gateway, + jint dns1, + jint dns2) +{ + int result; + uint32_t lease; + + const char *nameStr = env->GetStringUTFChars(ifname, NULL); + result = ::ifc_configure(nameStr, ipaddr, mask, gateway, dns1, dns2); + env->ReleaseStringUTFChars(ifname, nameStr); + return (jboolean)(result == 0); +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gNetworkUtilMethods[] = { + /* name, signature, funcPtr */ + + { "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface }, + { "addHostRoute", "(Ljava/lang/String;I)I", (void *)android_net_utils_addHostRoute }, + { "removeHostRoutes", "(Ljava/lang/String;)I", (void *)android_net_utils_removeHostRoutes }, + { "setDefaultRoute", "(Ljava/lang/String;I)I", (void *)android_net_utils_setDefaultRoute }, + { "getDefaultRoute", "(Ljava/lang/String;)I", (void *)android_net_utils_getDefaultRoute }, + { "removeDefaultRoute", "(Ljava/lang/String;)I", (void *)android_net_utils_removeDefaultRoute }, + { "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 }, + { "configureNative", "(Ljava/lang/String;IIIII)Z", (void *)android_net_utils_configureInterface }, + { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError }, +}; + +int register_android_net_NetworkUtils(JNIEnv* env) +{ + jclass netutils = env->FindClass(NETUTILS_PKG_NAME); + LOG_FATAL_IF(netutils == NULL, "Unable to find class " NETUTILS_PKG_NAME); + + dhcpInfoFieldIds.dhcpInfoClass = env->FindClass("android/net/DhcpInfo"); + if (dhcpInfoFieldIds.dhcpInfoClass != NULL) { + dhcpInfoFieldIds.constructorId = env->GetMethodID(dhcpInfoFieldIds.dhcpInfoClass, "<init>", "()V"); + dhcpInfoFieldIds.ipaddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "ipAddress", "I"); + dhcpInfoFieldIds.gateway = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "gateway", "I"); + dhcpInfoFieldIds.netmask = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "netmask", "I"); + dhcpInfoFieldIds.dns1 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns1", "I"); + dhcpInfoFieldIds.dns2 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns2", "I"); + dhcpInfoFieldIds.serverAddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "serverAddress", "I"); + dhcpInfoFieldIds.leaseDuration = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "leaseDuration", "I"); + } + + return AndroidRuntime::registerNativeMethods(env, + NETUTILS_PKG_NAME, gNetworkUtilMethods, NELEM(gNetworkUtilMethods)); +} + +}; // namespace android diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp new file mode 100644 index 0000000..48af99e --- /dev/null +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -0,0 +1,492 @@ +/* + * 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 "wifi" + +#include "jni.h" +#include <utils/misc.h> +#include <android_runtime/AndroidRuntime.h> +#include <utils/Log.h> + +#include "wifi.h" + +#define WIFI_PKG_NAME "android/net/wifi/WifiNative" + +namespace android { + +/* + * The following remembers the jfieldID's of the fields + * of the DhcpInfo Java object, so that we don't have + * to look them up every time. + */ +static struct fieldIds { + jclass dhcpInfoClass; + jmethodID constructorId; + jfieldID ipaddress; + jfieldID gateway; + jfieldID netmask; + jfieldID dns1; + jfieldID dns2; + jfieldID serverAddress; + jfieldID leaseDuration; +} dhcpInfoFieldIds; + +static int doCommand(const char *cmd, char *replybuf, int replybuflen) +{ + size_t reply_len = replybuflen - 1; + + if (::wifi_command(cmd, replybuf, &reply_len) != 0) + return -1; + else { + // Strip off trailing newline + if (reply_len > 0 && replybuf[reply_len-1] == '\n') + replybuf[reply_len-1] = '\0'; + else + replybuf[reply_len] = '\0'; + return 0; + } +} + +static jint doIntCommand(const char *cmd) +{ + char reply[256]; + + if (doCommand(cmd, reply, sizeof(reply)) != 0) { + return (jint)-1; + } else { + return (jint)atoi(reply); + } +} + +static jboolean doBooleanCommand(const char *cmd, const char *expect) +{ + char reply[256]; + + if (doCommand(cmd, reply, sizeof(reply)) != 0) { + return (jboolean)JNI_FALSE; + } else { + return (jboolean)(strcmp(reply, expect) == 0); + } +} + +// Send a command to the supplicant, and return the reply as a String +static jstring doStringCommand(JNIEnv *env, const char *cmd) +{ + char reply[4096]; + + if (doCommand(cmd, reply, sizeof(reply)) != 0) { + return env->NewStringUTF(NULL); + } else { + return env->NewStringUTF(reply); + } +} + +static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject clazz) +{ + return (jboolean)(::wifi_load_driver() == 0); +} + +static jboolean android_net_wifi_unloadDriver(JNIEnv* env, jobject clazz) +{ + return (jboolean)(::wifi_unload_driver() == 0); +} + +static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject clazz) +{ + return (jboolean)(::wifi_start_supplicant() == 0); +} + +static jboolean android_net_wifi_stopSupplicant(JNIEnv* env, jobject clazz) +{ + return (jboolean)(::wifi_stop_supplicant() == 0); +} + +static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject clazz) +{ + return (jboolean)(::wifi_connect_to_supplicant() == 0); +} + +static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject clazz) +{ + ::wifi_close_supplicant_connection(); +} + +static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject clazz) +{ + char buf[256]; + + int nread = ::wifi_wait_for_event(buf, sizeof buf); + if (nread > 0) { + return env->NewStringUTF(buf); + } else { + return env->NewStringUTF(NULL); + } +} + +static jstring android_net_wifi_listNetworksCommand(JNIEnv* env, jobject clazz) +{ + return doStringCommand(env, "LIST_NETWORKS"); +} + +static jint android_net_wifi_addNetworkCommand(JNIEnv* env, jobject clazz) +{ + return doIntCommand("ADD_NETWORK"); +} + +static jboolean android_net_wifi_setNetworkVariableCommand(JNIEnv* env, + jobject clazz, + jint netId, + jstring name, + jstring value) +{ + char cmdstr[256]; + jboolean isCopy; + + const char *nameStr = env->GetStringUTFChars(name, &isCopy); + const char *valueStr = env->GetStringUTFChars(value, &isCopy); + + if (nameStr == NULL || valueStr == NULL) + return JNI_FALSE; + + int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "SET_NETWORK %d %s %s", + netId, nameStr, valueStr) >= (int)sizeof(cmdstr); + + env->ReleaseStringUTFChars(name, nameStr); + env->ReleaseStringUTFChars(value, valueStr); + + return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); +} + +static jstring android_net_wifi_getNetworkVariableCommand(JNIEnv* env, + jobject clazz, + jint netId, + jstring name) +{ + char cmdstr[256]; + jboolean isCopy; + + const char *nameStr = env->GetStringUTFChars(name, &isCopy); + + if (nameStr == NULL) + return env->NewStringUTF(NULL); + + int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "GET_NETWORK %d %s", + netId, nameStr) >= (int)sizeof(cmdstr); + + env->ReleaseStringUTFChars(name, nameStr); + + return cmdTooLong ? env->NewStringUTF(NULL) : doStringCommand(env, cmdstr); +} + +static jboolean android_net_wifi_removeNetworkCommand(JNIEnv* env, jobject clazz, jint netId) +{ + char cmdstr[256]; + + int numWritten = snprintf(cmdstr, sizeof(cmdstr), "REMOVE_NETWORK %d", netId); + int cmdTooLong = numWritten >= (int)sizeof(cmdstr); + + return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); +} + +static jboolean android_net_wifi_enableNetworkCommand(JNIEnv* env, + jobject clazz, + jint netId, + jboolean disableOthers) +{ + char cmdstr[256]; + const char *cmd = disableOthers ? "SELECT_NETWORK" : "ENABLE_NETWORK"; + + int numWritten = snprintf(cmdstr, sizeof(cmdstr), "%s %d", cmd, netId); + int cmdTooLong = numWritten >= (int)sizeof(cmdstr); + + return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); +} + +static jboolean android_net_wifi_disableNetworkCommand(JNIEnv* env, jobject clazz, jint netId) +{ + char cmdstr[256]; + + int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DISABLE_NETWORK %d", netId); + int cmdTooLong = numWritten >= (int)sizeof(cmdstr); + + return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); +} + +static jstring android_net_wifi_statusCommand(JNIEnv* env, jobject clazz) +{ + return doStringCommand(env, "STATUS"); +} + +static jboolean android_net_wifi_pingCommand(JNIEnv* env, jobject clazz) +{ + return doBooleanCommand("PING", "PONG"); +} + +static jstring android_net_wifi_scanResultsCommand(JNIEnv* env, jobject clazz) +{ + return doStringCommand(env, "SCAN_RESULTS"); +} + +static jboolean android_net_wifi_disconnectCommand(JNIEnv* env, jobject clazz) +{ + return doBooleanCommand("DISCONNECT", "OK"); +} + +static jboolean android_net_wifi_reconnectCommand(JNIEnv* env, jobject clazz) +{ + return doBooleanCommand("RECONNECT", "OK"); +} +static jboolean android_net_wifi_reassociateCommand(JNIEnv* env, jobject clazz) +{ + return doBooleanCommand("REASSOCIATE", "OK"); +} + +static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject clazz) +{ + jboolean result; + // Ignore any error from setting the scan mode. + // The scan will still work. + (void)doBooleanCommand("DRIVER SCAN-ACTIVE", "OK"); + result = doBooleanCommand("SCAN", "OK"); + (void)doBooleanCommand("DRIVER SCAN-PASSIVE", "OK"); + return result; +} + +static jboolean android_net_wifi_setScanModeCommand(JNIEnv* env, jobject clazz, jboolean setActive) +{ + jboolean result; + // Ignore any error from setting the scan mode. + // The scan will still work. + if (setActive) { + return doBooleanCommand("DRIVER SCAN-ACTIVE", "OK"); + } else { + return doBooleanCommand("DRIVER SCAN-PASSIVE", "OK"); + } +} + +static jboolean android_net_wifi_startDriverCommand(JNIEnv* env, jobject clazz) +{ + return doBooleanCommand("DRIVER START", "OK"); +} + +static jboolean android_net_wifi_stopDriverCommand(JNIEnv* env, jobject clazz) +{ + return doBooleanCommand("DRIVER STOP", "OK"); +} + +static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz) +{ + char reply[256]; + int rssi = -200; + + if (doCommand("DRIVER RSSI", reply, sizeof(reply)) != 0) { + return (jint)-1; + } + // reply comes back in the form "<SSID> rssi XX" where XX is the + // number we're interested in. if we're associating, it returns "OK". + if (strcmp(reply, "OK") != 0) { + sscanf(reply, "%*s %*s %d", &rssi); + } + return (jint)rssi; +} + +static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject clazz) +{ + char reply[256]; + int linkspeed; + + if (doCommand("DRIVER LINKSPEED", reply, sizeof(reply)) != 0) { + return (jint)-1; + } + // reply comes back in the form "LinkSpeed XX" where XX is the + // number we're interested in. + sscanf(reply, "%*s %u", &linkspeed); + return (jint)linkspeed; +} + +static jstring android_net_wifi_getMacAddressCommand(JNIEnv* env, jobject clazz) +{ + char reply[256]; + char buf[256]; + + if (doCommand("DRIVER MACADDR", reply, sizeof(reply)) != 0) { + return env->NewStringUTF(NULL); + } + // reply comes back in the form "Macaddr = XX.XX.XX.XX.XX.XX" where XX + // is the part of the string we're interested in. + if (sscanf(reply, "%*s = %255s", buf) == 1) + return env->NewStringUTF(buf); + else + return env->NewStringUTF(NULL); +} + +static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject clazz, jint mode) +{ + char cmdstr[256]; + + int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER POWERMODE %d", mode); + int cmdTooLong = numWritten >= (int)sizeof(cmdstr); + + return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); +} + +static jboolean android_net_wifi_setBluetoothCoexistenceModeCommand(JNIEnv* env, jobject clazz, jint mode) +{ + char cmdstr[256]; + + int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER BTCOEXMODE %d", mode); + int cmdTooLong = numWritten >= (int)sizeof(cmdstr); + + return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); +} + +static jboolean android_net_wifi_saveConfigCommand(JNIEnv* env, jobject clazz) +{ + // Make sure we never write out a value for AP_SCAN other than 1 + (void)doBooleanCommand("AP_SCAN 1", "OK"); + return doBooleanCommand("SAVE_CONFIG", "OK"); +} + +static jboolean android_net_wifi_reloadConfigCommand(JNIEnv* env, jobject clazz) +{ + return doBooleanCommand("RECONFIGURE", "OK"); +} + +static jboolean android_net_wifi_setScanResultHandlingCommand(JNIEnv* env, jobject clazz, jint mode) +{ + char cmdstr[256]; + + int numWritten = snprintf(cmdstr, sizeof(cmdstr), "AP_SCAN %d", mode); + int cmdTooLong = numWritten >= (int)sizeof(cmdstr); + + return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); +} + +static jboolean android_net_wifi_addToBlacklistCommand(JNIEnv* env, jobject clazz, jstring bssid) +{ + char cmdstr[256]; + jboolean isCopy; + + const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy); + + int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "BLACKLIST %s", bssidStr) >= sizeof(cmdstr); + + env->ReleaseStringUTFChars(bssid, bssidStr); + + return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); +} + +static jboolean android_net_wifi_clearBlacklistCommand(JNIEnv* env, jobject clazz) +{ + return doBooleanCommand("BLACKLIST clear", "OK"); +} + +static jboolean android_net_wifi_doDhcpRequest(JNIEnv* env, jobject clazz, jobject info) +{ + jint ipaddr, gateway, mask, dns1, dns2, server, lease; + jboolean succeeded = ((jboolean)::do_dhcp_request(&ipaddr, &gateway, &mask, + &dns1, &dns2, &server, &lease) == 0); + if (succeeded && dhcpInfoFieldIds.dhcpInfoClass != NULL) { + env->SetIntField(info, dhcpInfoFieldIds.ipaddress, ipaddr); + env->SetIntField(info, dhcpInfoFieldIds.gateway, gateway); + env->SetIntField(info, dhcpInfoFieldIds.netmask, mask); + env->SetIntField(info, dhcpInfoFieldIds.dns1, dns1); + env->SetIntField(info, dhcpInfoFieldIds.dns2, dns2); + env->SetIntField(info, dhcpInfoFieldIds.serverAddress, server); + env->SetIntField(info, dhcpInfoFieldIds.leaseDuration, lease); + } + return succeeded; +} + +static jstring android_net_wifi_getDhcpError(JNIEnv* env, jobject clazz) +{ + return env->NewStringUTF(::get_dhcp_error_string()); +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gWifiMethods[] = { + /* name, signature, funcPtr */ + + { "loadDriver", "()Z", (void *)android_net_wifi_loadDriver }, + { "unloadDriver", "()Z", (void *)android_net_wifi_unloadDriver }, + { "startSupplicant", "()Z", (void *)android_net_wifi_startSupplicant }, + { "stopSupplicant", "()Z", (void *)android_net_wifi_stopSupplicant }, + { "connectToSupplicant", "()Z", (void *)android_net_wifi_connectToSupplicant }, + { "closeSupplicantConnection", "()V", (void *)android_net_wifi_closeSupplicantConnection }, + + { "listNetworksCommand", "()Ljava/lang/String;", + (void*) android_net_wifi_listNetworksCommand }, + { "addNetworkCommand", "()I", (void*) android_net_wifi_addNetworkCommand }, + { "setNetworkVariableCommand", "(ILjava/lang/String;Ljava/lang/String;)Z", + (void*) android_net_wifi_setNetworkVariableCommand }, + { "getNetworkVariableCommand", "(ILjava/lang/String;)Ljava/lang/String;", + (void*) android_net_wifi_getNetworkVariableCommand }, + { "removeNetworkCommand", "(I)Z", (void*) android_net_wifi_removeNetworkCommand }, + { "enableNetworkCommand", "(IZ)Z", (void*) android_net_wifi_enableNetworkCommand }, + { "disableNetworkCommand", "(I)Z", (void*) android_net_wifi_disableNetworkCommand }, + { "waitForEvent", "()Ljava/lang/String;", (void*) android_net_wifi_waitForEvent }, + { "statusCommand", "()Ljava/lang/String;", (void*) android_net_wifi_statusCommand }, + { "scanResultsCommand", "()Ljava/lang/String;", (void*) android_net_wifi_scanResultsCommand }, + { "pingCommand", "()Z", (void *)android_net_wifi_pingCommand }, + { "disconnectCommand", "()Z", (void *)android_net_wifi_disconnectCommand }, + { "reconnectCommand", "()Z", (void *)android_net_wifi_reconnectCommand }, + { "reassociateCommand", "()Z", (void *)android_net_wifi_reassociateCommand }, + { "scanCommand", "()Z", (void*) android_net_wifi_scanCommand }, + { "setScanModeCommand", "(Z)Z", (void*) android_net_wifi_setScanModeCommand }, + { "startDriverCommand", "()Z", (void*) android_net_wifi_startDriverCommand }, + { "stopDriverCommand", "()Z", (void*) android_net_wifi_stopDriverCommand }, + { "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand }, + { "setBluetoothCoexistenceModeCommand", "(I)Z", + (void*) android_net_wifi_setBluetoothCoexistenceModeCommand }, + { "getRssiCommand", "()I", (void*) android_net_wifi_getRssiCommand }, + { "getLinkSpeedCommand", "()I", (void*) android_net_wifi_getLinkSpeedCommand }, + { "getMacAddressCommand", "()Ljava/lang/String;", (void*) android_net_wifi_getMacAddressCommand }, + { "saveConfigCommand", "()Z", (void*) android_net_wifi_saveConfigCommand }, + { "reloadConfigCommand", "()Z", (void*) android_net_wifi_reloadConfigCommand }, + { "setScanResultHandlingCommand", "(I)Z", (void*) android_net_wifi_setScanResultHandlingCommand }, + { "addToBlacklistCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_addToBlacklistCommand }, + { "clearBlacklistCommand", "()Z", (void*) android_net_wifi_clearBlacklistCommand }, + + { "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest }, + { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError }, +}; + +int register_android_net_wifi_WifiManager(JNIEnv* env) +{ + jclass wifi = env->FindClass(WIFI_PKG_NAME); + LOG_FATAL_IF(wifi == NULL, "Unable to find class " WIFI_PKG_NAME); + + dhcpInfoFieldIds.dhcpInfoClass = env->FindClass("android/net/DhcpInfo"); + if (dhcpInfoFieldIds.dhcpInfoClass != NULL) { + dhcpInfoFieldIds.constructorId = env->GetMethodID(dhcpInfoFieldIds.dhcpInfoClass, "<init>", "()V"); + dhcpInfoFieldIds.ipaddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "ipAddress", "I"); + dhcpInfoFieldIds.gateway = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "gateway", "I"); + dhcpInfoFieldIds.netmask = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "netmask", "I"); + dhcpInfoFieldIds.dns1 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns1", "I"); + dhcpInfoFieldIds.dns2 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns2", "I"); + dhcpInfoFieldIds.serverAddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "serverAddress", "I"); + dhcpInfoFieldIds.leaseDuration = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "leaseDuration", "I"); + } + + return AndroidRuntime::registerNativeMethods(env, + WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods)); +} + +}; // namespace android diff --git a/core/jni/android_nio_utils.cpp b/core/jni/android_nio_utils.cpp new file mode 100644 index 0000000..584e7a4 --- /dev/null +++ b/core/jni/android_nio_utils.cpp @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#include "android_nio_utils.h" + +struct NioJNIData { + jclass nioAccessClass; + + jmethodID getBasePointerID; + jmethodID getBaseArrayID; + jmethodID getBaseArrayOffsetID; +}; + +static NioJNIData gNioJNI; + +void* android::nio_getPointer(JNIEnv *_env, jobject buffer, jarray *array) { + assert(array); + + jlong pointer; + jint offset; + void *data; + + pointer = _env->CallStaticLongMethod(gNioJNI.nioAccessClass, + gNioJNI.getBasePointerID, buffer); + if (pointer != 0L) { + *array = NULL; + return (void *) (jint) pointer; + } + + *array = (jarray) _env->CallStaticObjectMethod(gNioJNI.nioAccessClass, + gNioJNI.getBaseArrayID, buffer); + offset = _env->CallStaticIntMethod(gNioJNI.nioAccessClass, + gNioJNI.getBaseArrayOffsetID, buffer); + data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0); + + return (void *) ((char *) data + offset); +} + + +void android::nio_releasePointer(JNIEnv *_env, jarray array, void *data, + jboolean commit) { + _env->ReleasePrimitiveArrayCritical(array, data, + commit ? 0 : JNI_ABORT); +} + +/////////////////////////////////////////////////////////////////////////////// + +android::AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer, + jboolean commit) { + fEnv = env; + fCommit = commit; + fPointer = android::nio_getPointer(env, nioBuffer, &fArray); +} + +android::AutoBufferPointer::~AutoBufferPointer() { + if (NULL != fArray) { + android::nio_releasePointer(fEnv, fArray, fPointer, fCommit); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static jclass findClass(JNIEnv* env, const char name[]) { + jclass c = env->FindClass(name); + LOG_FATAL_IF(!c, "Unable to find class %s", name); + return c; +} + +static jmethodID findStaticMethod(JNIEnv* env, jclass c, const char method[], + const char params[]) { + jmethodID m = env->GetStaticMethodID(c, method, params); + LOG_FATAL_IF(!m, "Unable to find method %s", method); + return m; +} + +static jfieldID getFieldID(JNIEnv* env, jclass c, const char name[], + const char type[]) { + jfieldID f = env->GetFieldID(c, name, type); + LOG_FATAL_IF(!f, "Unable to find field %s", name); + return f; +} + +namespace android { + +int register_android_nio_utils(JNIEnv* env); +int register_android_nio_utils(JNIEnv* env) { + jclass localClass = findClass(env, "java/nio/NIOAccess"); + gNioJNI.getBasePointerID = findStaticMethod(env, localClass, + "getBasePointer", "(Ljava/nio/Buffer;)J"); + gNioJNI.getBaseArrayID = findStaticMethod(env, localClass, + "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); + gNioJNI.getBaseArrayOffsetID = findStaticMethod(env, localClass, + "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); + + // now record a permanent version of the class ID + gNioJNI.nioAccessClass = (jclass) env->NewGlobalRef(localClass); + + return 0; +} + +} diff --git a/core/jni/android_nio_utils.h b/core/jni/android_nio_utils.h new file mode 100644 index 0000000..69c360c --- /dev/null +++ b/core/jni/android_nio_utils.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#ifndef android_nio_utils_DEFINED +#define android_nio_utils_DEFINED + +#include <android_runtime/AndroidRuntime.h> + +namespace android { + +/** + * Given an nio.Buffer, return a pointer to it, beginning at its current + * position. The returned pointer is only valid for the current JNI stack-frame. + * For performance, it does not create any global references, so the getPointer + * (and releasePointer if array is returned non-null) must be done in the + * same JNI stack-frame. + * + * @param env The current JNI env + * @param buffer The nio.Buffer object + * @param array REQUIRED. Output. If on return it is set to non-null, then + * nio_releasePointer must be called with the array + * and the returned pointer when the caller is through with it. + * If on return it is set to null, do not call + * nio_releasePointer. + * @return The pointer to the memory in the buffer object + */ +void* nio_getPointer(JNIEnv *env, jobject buffer, jarray *array); + +/** + * Call this if android_nio_getPointer returned non-null in its array parameter. + * Pass that array and the returned pointer when you are done accessing the + * pointer. If called (i.e. array is non-null), it must be called in the same + * JNI stack-frame as getPointer + * + * @param env The current JNI env + * @param buffer The array returned from android_nio_getPointer (!= null) + * @param pointer The pointer returned by android_nio_getPointer + * @param commit JNI_FALSE if the pointer was just read, and JNI_TRUE if + * the pointer was written to. + */ +void nio_releasePointer(JNIEnv *env, jarray array, void *pointer, + jboolean commit); + +class AutoBufferPointer { +public: + AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit); + ~AutoBufferPointer(); + + void* pointer() const { return fPointer; } + +private: + JNIEnv* fEnv; + void* fPointer; + jarray fArray; + jint fRemaining; + jboolean fCommit; +}; + +} /* namespace android */ + +#endif diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp new file mode 100644 index 0000000..6ba949c --- /dev/null +++ b/core/jni/android_os_Debug.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JNIHelp.h" +#include "jni.h" +#include "utils/misc.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <sys/time.h> + +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif + +namespace android +{ + +static jfieldID dalvikPss_field; +static jfieldID dalvikPrivateDirty_field; +static jfieldID dalvikSharedDirty_field; +static jfieldID nativePss_field; +static jfieldID nativePrivateDirty_field; +static jfieldID nativeSharedDirty_field; +static jfieldID otherPss_field; +static jfieldID otherPrivateDirty_field; +static jfieldID otherSharedDirty_field; + +struct stats_t { + int dalvikPss; + int dalvikPrivateDirty; + int dalvikSharedDirty; + + int nativePss; + int nativePrivateDirty; + int nativeSharedDirty; + + int otherPss; + int otherPrivateDirty; + int otherSharedDirty; +}; + +#define BINDER_STATS "/proc/binder/stats" + +static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz) +{ +#ifdef HAVE_MALLOC_H + struct mallinfo info = mallinfo(); + return (jlong) info.usmblks; +#else + return -1; +#endif +} + +static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz) +{ +#ifdef HAVE_MALLOC_H + struct mallinfo info = mallinfo(); + return (jlong) info.uordblks; +#else + return -1; +#endif +} + +static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz) +{ +#ifdef HAVE_MALLOC_H + struct mallinfo info = mallinfo(); + return (jlong) info.fordblks; +#else + return -1; +#endif +} + +static int read_mapinfo(FILE *fp, stats_t* stats) +{ + char line[1024]; + int len; + int skip; + + unsigned start = 0, size = 0, resident = 0, pss = 0; + unsigned shared_clean = 0, shared_dirty = 0; + unsigned private_clean = 0, private_dirty = 0; + unsigned referenced = 0; + + int isNativeHeap; + int isDalvikHeap; + int isSqliteHeap; + +again: + isNativeHeap = 0; + isDalvikHeap = 0; + isSqliteHeap = 0; + skip = 0; + + if(fgets(line, 1024, fp) == 0) return 0; + + len = strlen(line); + if (len < 1) return 0; + line[--len] = 0; + + /* ignore guard pages */ + if (line[18] == '-') skip = 1; + + start = strtoul(line, 0, 16); + + if (len >= 50) { + if (!strcmp(line + 49, "[heap]")) { + isNativeHeap = 1; + } else if (!strncmp(line + 49, "/dalvik-LinearAlloc", strlen("/dalvik-LinearAlloc"))) { + isDalvikHeap = 1; + } else if (!strncmp(line + 49, "/mspace/dalvik-heap", strlen("/mspace/dalvik-heap"))) { + isDalvikHeap = 1; + } else if (!strncmp(line + 49, "/dalvik-heap-bitmap/", strlen("/dalvik-heap-bitmap/"))) { + isDalvikHeap = 1; + } else if (!strncmp(line + 49, "/tmp/sqlite-heap", strlen("/tmp/sqlite-heap"))) { + isSqliteHeap = 1; + } + } + + // TODO: This needs to be fixed to be less fragile. If the order of this file changes or a new + // line is add, this method will return without filling out any of the information. + + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Size: %d kB", &size) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Rss: %d kB", &resident) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Pss: %d kB", &pss) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Shared_Clean: %d kB", &shared_clean) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Shared_Dirty: %d kB", &shared_dirty) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Private_Clean: %d kB", &private_clean) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Private_Dirty: %d kB", &private_dirty) != 1) return 0; + if (fgets(line, 1024, fp) == 0) return 0; + if (sscanf(line, "Referenced: %d kB", &referenced) != 1) return 0; + + if (skip) { + goto again; + } + + if (isNativeHeap) { + stats->nativePss += pss; + stats->nativePrivateDirty += private_dirty; + stats->nativeSharedDirty += shared_dirty; + } else if (isDalvikHeap) { + stats->dalvikPss += pss; + stats->dalvikPrivateDirty += private_dirty; + stats->dalvikSharedDirty += shared_dirty; + } else if (isSqliteHeap) { + // ignore + } else { + stats->otherPss += pss; + stats->otherPrivateDirty += shared_dirty; + stats->otherSharedDirty += private_dirty; + } + + return 1; +} + +static void load_maps(int pid, stats_t* stats) +{ + char tmp[128]; + FILE *fp; + + sprintf(tmp, "/proc/%d/smaps", pid); + fp = fopen(tmp, "r"); + if (fp == 0) return; + + while (read_mapinfo(fp, stats) != 0) { + // Do nothing + } + fclose(fp); +} + +static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object) +{ + stats_t stats; + memset(&stats, 0, sizeof(stats_t)); + + load_maps(getpid(), &stats); + + env->SetIntField(object, dalvikPss_field, stats.dalvikPss); + env->SetIntField(object, dalvikPrivateDirty_field, stats.dalvikPrivateDirty); + env->SetIntField(object, dalvikSharedDirty_field, stats.dalvikSharedDirty); + + env->SetIntField(object, nativePss_field, stats.nativePss); + env->SetIntField(object, nativePrivateDirty_field, stats.nativePrivateDirty); + env->SetIntField(object, nativeSharedDirty_field, stats.nativeSharedDirty); + + env->SetIntField(object, otherPss_field, stats.otherPss); + env->SetIntField(object, otherPrivateDirty_field, stats.otherPrivateDirty); + env->SetIntField(object, otherSharedDirty_field, stats.otherSharedDirty); +} + +static jint read_binder_stat(const char* stat) +{ + FILE* fp = fopen(BINDER_STATS, "r"); + if (fp == NULL) { + return -1; + } + + char line[1024]; + + char compare[128]; + int len = snprintf(compare, 128, "proc %d", getpid()); + + // loop until we have the block that represents this process + do { + if (fgets(line, 1024, fp) == 0) { + return -1; + } + } while (strncmp(compare, line, len)); + + // now that we have this process, read until we find the stat that we are looking for + len = snprintf(compare, 128, " %s: ", stat); + + do { + if (fgets(line, 1024, fp) == 0) { + return -1; + } + } while (strncmp(compare, line, len)); + + // we have the line, now increment the line ptr to the value + char* ptr = line + len; + return atoi(ptr); +} + +static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz) +{ + return read_binder_stat("bcTRANSACTION"); +} + +static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz) +{ + return read_binder_stat("brTRANSACTION"); +} + +// these are implemented in android_util_Binder.cpp +jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz); +jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz); +jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz); + +/* + * JNI registration. + */ + +static JNINativeMethod gMethods[] = { + { "getNativeHeapSize", "()J", + (void*) android_os_Debug_getNativeHeapSize }, + { "getNativeHeapAllocatedSize", "()J", + (void*) android_os_Debug_getNativeHeapAllocatedSize }, + { "getNativeHeapFreeSize", "()J", + (void*) android_os_Debug_getNativeHeapFreeSize }, + { "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V", + (void*) android_os_Debug_getDirtyPages }, + { "getBinderSentTransactions", "()I", + (void*) android_os_Debug_getBinderSentTransactions }, + { "getBinderReceivedTransactions", "()I", + (void*) android_os_getBinderReceivedTransactions }, + { "getBinderLocalObjectCount", "()I", + (void*)android_os_Debug_getLocalObjectCount }, + { "getBinderProxyObjectCount", "()I", + (void*)android_os_Debug_getProxyObjectCount }, + { "getBinderDeathObjectCount", "()I", + (void*)android_os_Debug_getDeathObjectCount }, +}; + +int register_android_os_Debug(JNIEnv *env) +{ + jclass clazz = env->FindClass("android/os/Debug$MemoryInfo"); + + dalvikPss_field = env->GetFieldID(clazz, "dalvikPss", "I"); + dalvikPrivateDirty_field = env->GetFieldID(clazz, "dalvikPrivateDirty", "I"); + dalvikSharedDirty_field = env->GetFieldID(clazz, "dalvikSharedDirty", "I"); + + nativePss_field = env->GetFieldID(clazz, "nativePss", "I"); + nativePrivateDirty_field = env->GetFieldID(clazz, "nativePrivateDirty", "I"); + nativeSharedDirty_field = env->GetFieldID(clazz, "nativeSharedDirty", "I"); + + otherPss_field = env->GetFieldID(clazz, "otherPss", "I"); + otherPrivateDirty_field = env->GetFieldID(clazz, "otherPrivateDirty", "I"); + otherSharedDirty_field = env->GetFieldID(clazz, "otherSharedDirty", "I"); + + return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods)); +} + +}; diff --git a/core/jni/android_os_Exec.cpp b/core/jni/android_os_Exec.cpp new file mode 100644 index 0000000..ca5e695 --- /dev/null +++ b/core/jni/android_os_Exec.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Exec" + +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" +#include "android_runtime/AndroidRuntime.h" + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <termios.h> + +namespace android +{ + +static jclass class_fileDescriptor; +static jfieldID field_fileDescriptor_descriptor; +static jmethodID method_fileDescriptor_init; + + +static int create_subprocess(const char *cmd, const char *arg0, const char *arg1, + int* pProcessId) +{ + char *devname; + int ptm; + pid_t pid; + + ptm = open("/dev/ptmx", O_RDWR); // | O_NOCTTY); + if(ptm < 0){ + LOGE("[ cannot open /dev/ptmx - %s ]\n",strerror(errno)); + return -1; + } + fcntl(ptm, F_SETFD, FD_CLOEXEC); + + if(grantpt(ptm) || unlockpt(ptm) || + ((devname = (char*) ptsname(ptm)) == 0)){ + LOGE("[ trouble with /dev/ptmx - %s ]\n", strerror(errno)); + return -1; + } + + pid = fork(); + if(pid < 0) { + LOGE("- fork failed: %s -\n", strerror(errno)); + return -1; + } + + if(pid == 0){ + int pts; + + setsid(); + + pts = open(devname, O_RDWR); + if(pts < 0) exit(-1); + + dup2(pts, 0); + dup2(pts, 1); + dup2(pts, 2); + + close(ptm); + + execl(cmd, cmd, arg0, arg1, NULL); + exit(-1); + } else { + *pProcessId = (int) pid; + return ptm; + } +} + + +static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, + jstring cmd, jstring arg0, jstring arg1, jintArray processIdArray) +{ + const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0; + String8 cmd_8; + if (str) { + cmd_8 = String8(str, env->GetStringLength(cmd)); + env->ReleaseStringCritical(cmd, str); + } + + str = arg0 ? env->GetStringCritical(arg0, 0) : 0; + const char* arg0Str = 0; + String8 arg0_8; + if (str) { + arg0_8 = String8(str, env->GetStringLength(arg0)); + env->ReleaseStringCritical(arg0, str); + arg0Str = arg0_8.string(); + } + + str = arg1 ? env->GetStringCritical(arg1, 0) : 0; + const char* arg1Str = 0; + String8 arg1_8; + if (str) { + arg1_8 = String8(str, env->GetStringLength(arg1)); + env->ReleaseStringCritical(arg1, str); + arg1Str = arg1_8.string(); + } + + int procId; + int ptm = create_subprocess(cmd_8.string(), arg0Str, arg1Str, &procId); + + if (processIdArray) { + int procIdLen = env->GetArrayLength(processIdArray); + if (procIdLen > 0) { + jboolean isCopy; + + int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy); + if (pProcId) { + *pProcId = procId; + env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0); + } + } + } + + jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init); + + if (!result) { + LOGE("Couldn't create a FileDescriptor."); + } + else { + env->SetIntField(result, field_fileDescriptor_descriptor, ptm); + } + + return result; +} + + +static void android_os_Exec_setPtyWindowSize(JNIEnv *env, jobject clazz, + jobject fileDescriptor, jint row, jint col, jint xpixel, jint ypixel) +{ + int fd; + struct winsize sz; + + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + sz.ws_row = row; + sz.ws_col = col; + sz.ws_xpixel = xpixel; + sz.ws_ypixel = ypixel; + + ioctl(fd, TIOCSWINSZ, &sz); +} + +static int android_os_Exec_waitFor(JNIEnv *env, jobject clazz, + jint procId) { + int status; + waitpid(procId, &status, 0); + int result = 0; + if (WIFEXITED(status)) { + result = WEXITSTATUS(status); + } + return result; +} + +static JNINativeMethod method_table[] = { + { "createSubprocess", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)Ljava/io/FileDescriptor;", + (void*) android_os_Exec_createSubProcess }, + { "setPtyWindowSize", "(Ljava/io/FileDescriptor;IIII)V", + (void*) android_os_Exec_setPtyWindowSize}, + { "waitFor", "(I)I", + (void*) android_os_Exec_waitFor} +}; + +int register_android_os_Exec(JNIEnv *env) +{ + class_fileDescriptor = env->FindClass("java/io/FileDescriptor"); + + if (class_fileDescriptor == NULL) { + LOGE("Can't find java/io/FileDescriptor"); + return -1; + } + + field_fileDescriptor_descriptor = env->GetFieldID(class_fileDescriptor, "descriptor", "I"); + + if (field_fileDescriptor_descriptor == NULL) { + LOGE("Can't find FileDescriptor.descriptor"); + return -1; + } + + method_fileDescriptor_init = env->GetMethodID(class_fileDescriptor, "<init>", "()V"); + if (method_fileDescriptor_init == NULL) { + LOGE("Can't find FileDescriptor.init"); + return -1; + } + + return AndroidRuntime::registerNativeMethods( + env, "android/os/Exec", + method_table, NELEM(method_table)); +} + +}; diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp new file mode 100644 index 0000000..21cb919 --- /dev/null +++ b/core/jni/android_os_FileUtils.cpp @@ -0,0 +1,208 @@ +/* //device/libs/android_runtime/android_util_Process.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "FileUtils" + +#include <utils/Log.h> + +#include <android_runtime/AndroidRuntime.h> + +#include "JNIHelp.h" + +#include <sys/errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <signal.h> + +#if HAVE_ANDROID_OS +#include <sys/ioctl.h> +#include <linux/msdos_fs.h> +#endif + +namespace android { + +static jclass gFileStatusClass; +static jfieldID gFileStatusDevFieldID; +static jfieldID gFileStatusInoFieldID; +static jfieldID gFileStatusModeFieldID; +static jfieldID gFileStatusNlinkFieldID; +static jfieldID gFileStatusUidFieldID; +static jfieldID gFileStatusGidFieldID; +static jfieldID gFileStatusSizeFieldID; +static jfieldID gFileStatusBlksizeFieldID; +static jfieldID gFileStatusBlocksFieldID; +static jfieldID gFileStatusAtimeFieldID; +static jfieldID gFileStatusMtimeFieldID; +static jfieldID gFileStatusCtimeFieldID; + +jint android_os_FileUtils_setPermissions(JNIEnv* env, jobject clazz, + jstring file, jint mode, + jint uid, jint gid) +{ + #if HAVE_ANDROID_OS + const jchar* str = env->GetStringCritical(file, 0); + String8 file8; + if (str) { + file8 = String8(str, env->GetStringLength(file)); + env->ReleaseStringCritical(file, str); + } + if (file8.size() <= 0) { + return ENOENT; + } + if (uid >= 0 || gid >= 0) { + int res = chown(file8.string(), uid, gid); + if (res != 0) { + return errno; + } + } + return chmod(file8.string(), mode) == 0 ? 0 : errno; + #else + return ENOSYS; + #endif +} + +jint android_os_FileUtils_getPermissions(JNIEnv* env, jobject clazz, + jstring file, jintArray outArray) +{ + #if HAVE_ANDROID_OS + const jchar* str = env->GetStringCritical(file, 0); + String8 file8; + if (str) { + file8 = String8(str, env->GetStringLength(file)); + env->ReleaseStringCritical(file, str); + } + if (file8.size() <= 0) { + return ENOENT; + } + struct stat st; + if (stat(file8.string(), &st) != 0) { + return errno; + } + jint* array = (jint*)env->GetPrimitiveArrayCritical(outArray, 0); + if (array) { + int len = env->GetArrayLength(outArray); + if (len >= 1) { + array[0] = st.st_mode; + } + if (len >= 2) { + array[1] = st.st_uid; + } + if (len >= 3) { + array[2] = st.st_gid; + } + } + env->ReleasePrimitiveArrayCritical(outArray, array, 0); + return 0; + #else + return ENOSYS; + #endif +} + +jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path) +{ + #if HAVE_ANDROID_OS + if (path == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return -1; + } + const char *pathStr = env->GetStringUTFChars(path, NULL); + int result = -1; + // only if our system supports this ioctl + #ifdef VFAT_IOCTL_GET_VOLUME_ID + int fd = open(pathStr, O_RDONLY); + if (fd >= 0) { + result = ioctl(fd, VFAT_IOCTL_GET_VOLUME_ID); + close(fd); + } + #endif + + env->ReleaseStringUTFChars(path, pathStr); + return result; + #else + return -1; + #endif +} + +jboolean android_os_FileUtils_getFileStatus(JNIEnv* env, jobject clazz, jstring path, jobject fileStatus) { + const char* pathStr = env->GetStringUTFChars(path, NULL); + jboolean ret = false; + + struct stat s; + int res = stat(pathStr, &s); + if (res == 0) { + ret = true; + if (fileStatus != NULL) { + env->SetIntField(fileStatus, gFileStatusDevFieldID, s.st_dev); + env->SetIntField(fileStatus, gFileStatusInoFieldID, s.st_ino); + env->SetIntField(fileStatus, gFileStatusModeFieldID, s.st_mode); + env->SetIntField(fileStatus, gFileStatusNlinkFieldID, s.st_nlink); + env->SetIntField(fileStatus, gFileStatusUidFieldID, s.st_uid); + env->SetIntField(fileStatus, gFileStatusGidFieldID, s.st_gid); + env->SetLongField(fileStatus, gFileStatusSizeFieldID, s.st_size); + env->SetIntField(fileStatus, gFileStatusBlksizeFieldID, s.st_blksize); + env->SetLongField(fileStatus, gFileStatusBlocksFieldID, s.st_blocks); + env->SetLongField(fileStatus, gFileStatusAtimeFieldID, s.st_atime); + env->SetLongField(fileStatus, gFileStatusMtimeFieldID, s.st_mtime); + env->SetLongField(fileStatus, gFileStatusCtimeFieldID, s.st_ctime); + } + } + + env->ReleaseStringUTFChars(path, pathStr); + + return ret; +} + +static const JNINativeMethod methods[] = { + {"setPermissions", "(Ljava/lang/String;III)I", (void*)android_os_FileUtils_setPermissions}, + {"getPermissions", "(Ljava/lang/String;[I)I", (void*)android_os_FileUtils_getPermissions}, + {"getFatVolumeId", "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId}, + {"getFileStatus", "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z", (void*)android_os_FileUtils_getFileStatus}, +}; + +static const char* const kFileUtilsPathName = "android/os/FileUtils"; + +int register_android_os_FileUtils(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass(kFileUtilsPathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.FileUtils"); + + gFileStatusClass = env->FindClass("android/os/FileUtils$FileStatus"); + LOG_FATAL_IF(gFileStatusClass == NULL, "Unable to find class android.os.FileUtils$FileStatus"); + + gFileStatusDevFieldID = env->GetFieldID(gFileStatusClass, "dev", "I"); + gFileStatusInoFieldID = env->GetFieldID(gFileStatusClass, "ino", "I"); + gFileStatusModeFieldID = env->GetFieldID(gFileStatusClass, "mode", "I"); + gFileStatusNlinkFieldID = env->GetFieldID(gFileStatusClass, "nlink", "I"); + gFileStatusUidFieldID = env->GetFieldID(gFileStatusClass, "uid", "I"); + gFileStatusGidFieldID = env->GetFieldID(gFileStatusClass, "gid", "I"); + gFileStatusSizeFieldID = env->GetFieldID(gFileStatusClass, "size", "J"); + gFileStatusBlksizeFieldID = env->GetFieldID(gFileStatusClass, "blksize", "I"); + gFileStatusBlocksFieldID = env->GetFieldID(gFileStatusClass, "blocks", "J"); + gFileStatusAtimeFieldID = env->GetFieldID(gFileStatusClass, "atime", "J"); + gFileStatusMtimeFieldID = env->GetFieldID(gFileStatusClass, "mtime", "J"); + gFileStatusCtimeFieldID = env->GetFieldID(gFileStatusClass, "ctime", "J"); + + return AndroidRuntime::registerNativeMethods( + env, kFileUtilsPathName, + methods, NELEM(methods)); +} + +} + diff --git a/core/jni/android_os_Hardware.cpp b/core/jni/android_os_Hardware.cpp new file mode 100644 index 0000000..a302498 --- /dev/null +++ b/core/jni/android_os_Hardware.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include <hardware/flashlight.h> +#include <hardware/led.h> +#include <hardware/power.h> + +#include <nativehelper/jni.h> +#include <android_runtime/AndroidRuntime.h> +#include <nativehelper/JNIHelp.h> + +namespace android { + +static jboolean +setLedState(JNIEnv *env, jobject clazz, jint colorARGB, jint onMS, jint offMS) +{ + return set_led_state(colorARGB, onMS, offMS); +} + +static jint +getFlashlightEnabled(JNIEnv *env, jobject clazz) +{ + return get_flashlight_enabled(); +} + +static void +setFlashlightEnabled(JNIEnv *env, jobject clazz, jboolean on) +{ + set_flashlight_enabled(on); +} + +static void +enableCameraFlash(JNIEnv *env, jobject clazz, jint milliseconds) +{ + enable_camera_flash(milliseconds); +} + +static void +setScreenBacklight(JNIEnv *env, jobject clazz, jint brightness) +{ + set_light_brightness(SCREEN_LIGHT, brightness); +} + +static void +setKeyboardBacklight(JNIEnv *env, jobject clazz, jboolean on) +{ + set_light_brightness(KEYBOARD_LIGHT, (on ? 255 : 0)); +} + +static void +setButtonBacklight(JNIEnv *env, jobject clazz, jboolean on) +{ + set_light_brightness(BUTTON_LIGHT, (on ? 255 : 0)); +} + +// ============================================================================ +/* + * JNI registration. + */ + +static JNINativeMethod g_methods[] = { + /* name, signature, funcPtr */ + { "setLedState", "(III)I", (void*)setLedState }, + { "getFlashlightEnabled", "()Z", (void*)getFlashlightEnabled }, + { "setFlashlightEnabled", "(Z)V", (void*)setFlashlightEnabled }, + { "enableCameraFlash", "(I)V", (void*)enableCameraFlash }, + { "setScreenBacklight", "(I)V", (void*)setScreenBacklight }, + { "setKeyboardBacklight", "(Z)V", (void*)setKeyboardBacklight }, + { "setButtonBacklight", "(Z)V", (void*)setButtonBacklight }, +}; + +int register_android_os_Hardware(JNIEnv* env) +{ + return AndroidRuntime::registerNativeMethods(env, + "android/os/Hardware", g_methods, NELEM(g_methods)); +} + +}; // namespace android diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp new file mode 100644 index 0000000..edf7dc4 --- /dev/null +++ b/core/jni/android_os_MemoryFile.cpp @@ -0,0 +1,129 @@ +/* + * 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_TAG "MemoryFile" +#include <utils/Log.h> + +#include <cutils/ashmem.h> +#include <android_runtime/AndroidRuntime.h> +#include "JNIHelp.h" +#include <unistd.h> +#include <sys/mman.h> + + +namespace android { + +static jint android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length) +{ + const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL); + + // round up length to page boundary + length = (((length - 1) / getpagesize()) + 1) * getpagesize(); + int result = ashmem_create_region(namestr, length); + + if (name) + env->ReleaseStringUTFChars(name, namestr); + + if (result < 0) + jniThrowException(env, "java/io/IOException", "ashmem_create_region failed"); + return result; +} + +static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jint fd, jint length) +{ + jint result = (jint)mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (!result) + jniThrowException(env, "java/io/IOException", "mmap failed"); + return result; +} + +static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jint fd) +{ + close(fd); +} + +static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz, + jint fd, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, + jint count, jboolean unpinned) +{ + if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { + ashmem_unpin_region(fd, 0, 0); + jniThrowException(env, "java/io/IOException", "ashmem region was purged"); + return -1; + } + + jbyte* bytes = env->GetByteArrayElements(buffer, 0); + memcpy(bytes + destOffset, (const char *)address + srcOffset, count); + env->ReleaseByteArrayElements(buffer, bytes, 0); + + if (unpinned) { + ashmem_unpin_region(fd, 0, 0); + } + return count; +} + +static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz, + jint fd, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, + jint count, jboolean unpinned) +{ + if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { + ashmem_unpin_region(fd, 0, 0); + jniThrowException(env, "java/io/IOException", "ashmem region was purged"); + return -1; + } + + jbyte* bytes = env->GetByteArrayElements(buffer, 0); + memcpy((char *)address + destOffset, bytes + srcOffset, count); + env->ReleaseByteArrayElements(buffer, bytes, 0); + + if (unpinned) { + ashmem_unpin_region(fd, 0, 0); + } + return count; +} + +static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jint fd, jboolean pin) +{ + int result = (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0)); + if (result < 0) { + jniThrowException(env, "java/io/IOException", NULL); + } +} + +static const JNINativeMethod methods[] = { + {"native_open", "(Ljava/lang/String;I)I", (void*)android_os_MemoryFile_open}, + {"native_mmap", "(II)I", (void*)android_os_MemoryFile_mmap}, + {"native_close", "(I)V", (void*)android_os_MemoryFile_close}, + {"native_read", "(II[BIIIZ)I", (void*)android_os_MemoryFile_read}, + {"native_write", "(II[BIIIZ)V", (void*)android_os_MemoryFile_write}, + {"native_pin", "(IZ)V", (void*)android_os_MemoryFile_pin}, +}; + +static const char* const kClassPathName = "android/os/MemoryFile"; + +int register_android_os_MemoryFile(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass(kClassPathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.FileUtils"); + + return AndroidRuntime::registerNativeMethods( + env, kClassPathName, + methods, NELEM(methods)); +} + +} diff --git a/core/jni/android_os_NetStat.cpp b/core/jni/android_os_NetStat.cpp new file mode 100644 index 0000000..983f719 --- /dev/null +++ b/core/jni/android_os_NetStat.cpp @@ -0,0 +1,158 @@ +/* //device/libs/android_runtime/android_os_Wifi.cpp +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** 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 "NetStat" + +#include "jni.h" +#include <utils/misc.h> +#include <android_runtime/AndroidRuntime.h> +#include <utils/Log.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#if HAVE_ANDROID_OS +#include <utils/Atomic.h> +#endif + +namespace android { + +static jint android_os_netStatGetTxPkts(JNIEnv* env, jobject clazz) +{ + int ret = 0; + int fd = -1; + char input[50]; + + fd = open("/sys/class/net/rmnet0/statistics/tx_packets", O_RDONLY); + if (fd <= 0) { + fd = open("/sys/class/net/ppp0/statistics/tx_packets", O_RDONLY); + } + + if (fd > 0) { + int size = read(fd, input, 50); + if (size > 0) { + ret = atoi(input); + } + close(fd); + } + + return (jint)ret; +} + +static jint android_os_netStatGetRxPkts(JNIEnv* env, jobject clazz) +{ + int ret = 0; + int fd = -1; + char input[50]; + + fd = open("/sys/class/net/rmnet0/statistics/rx_packets", O_RDONLY); + if (fd <= 0) { + fd = open("/sys/class/net/ppp0/statistics/rx_packets", O_RDONLY); + } + + if (fd > 0) { + int size = read(fd, input, 50); + if (size > 0) { + ret = atoi(input); + } + close(fd); + } + + return (jint)ret; +} + +static jint android_os_netStatGetRxBytes(JNIEnv* env, jobject clazz) +{ + int ret = 0; + int fd = -1; + char input[50]; + + fd = open("/sys/class/net/rmnet0/statistics/rx_bytes", O_RDONLY); + if (fd <= 0) { + fd = open("/sys/class/net/ppp0/statistics/rx_bytes", O_RDONLY); + } + + if (fd > 0) { + int size = read(fd, input, 50); + if (size > 0) { + ret = atoi(input); + } + close(fd); + } + + return (jint)ret; +} + + +static jint android_os_netStatGetTxBytes(JNIEnv* env, jobject clazz) +{ + int ret = 0; + int fd = -1; + char input[50]; + + fd = open("/sys/class/net/rmnet0/statistics/tx_bytes", O_RDONLY); + if (fd <= 0) { + fd = open("/sys/class/net/ppp0/statistics/tx_bytes", O_RDONLY); + } + + if (fd > 0) { + int size = read(fd, input, 50); + if (size > 0) { + ret = atoi(input); + } + close(fd); + } + + return (jint)ret; +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + + { "netStatGetTxPkts", "()I", + (void*) android_os_netStatGetTxPkts }, + + { "netStatGetRxPkts", "()I", + (void*) android_os_netStatGetRxPkts }, + + { "netStatGetTxBytes", "()I", + (void*) android_os_netStatGetTxBytes }, + + { "netStatGetRxBytes", "()I", + (void*) android_os_netStatGetRxBytes }, + +}; + +int register_android_os_NetStat(JNIEnv* env) +{ + jclass netStat = env->FindClass("android/os/NetStat"); + LOG_FATAL_IF(netStat == NULL, "Unable to find class android/os/NetStat"); + + return AndroidRuntime::registerNativeMethods(env, + "android/os/NetStat", gMethods, NELEM(gMethods)); +} + +}; // namespace android + diff --git a/core/jni/android_os_ParcelFileDescriptor.cpp b/core/jni/android_os_ParcelFileDescriptor.cpp new file mode 100644 index 0000000..465e233 --- /dev/null +++ b/core/jni/android_os_ParcelFileDescriptor.cpp @@ -0,0 +1,102 @@ +/* //device/libs/android_runtime/android_os_ParcelFileDescriptor.cpp +** +** 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. +*/ + +#include "JNIHelp.h" + +#include <fcntl.h> +#include <sys/stat.h> +#include <stdio.h> + +#include <utils/Log.h> + +#include <android_runtime/AndroidRuntime.h> + +namespace android +{ + +static struct file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; + jfieldID mDescriptor; +} gFileDescriptorOffsets; + +static struct socket_offsets_t +{ + jfieldID mSocketImpl; +} gSocketOffsets; + +static struct socket_impl_offsets_t +{ + jfieldID mFileDescriptor; +} gSocketImplOffsets; + + +static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromSocket(JNIEnv* env, + jobject clazz, jobject object) +{ + jobject socketImpl = env->GetObjectField(object, gSocketOffsets.mSocketImpl); + jobject fileDescriptor = env->GetObjectField(socketImpl, gSocketImplOffsets.mFileDescriptor); + jint fd = env->GetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor); + jobject fileDescriptorClone = env->NewObject(gFileDescriptorOffsets.mClass, + gFileDescriptorOffsets.mConstructor); + if (fileDescriptorClone != NULL) { + env->SetIntField(fileDescriptorClone, gFileDescriptorOffsets.mDescriptor, dup(fd)); + } + return fileDescriptorClone; +} + +static const JNINativeMethod gParcelFileDescriptorMethods[] = { + {"getFileDescriptorFromSocket", "(Ljava/net/Socket;)Ljava/io/FileDescriptor;", + (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromSocket} +}; + +const char* const kParcelFileDescriptorPathName = "android/os/ParcelFileDescriptor"; + +int register_android_os_ParcelFileDescriptor(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass("java/net/Socket"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.net.Socket"); + gSocketOffsets.mSocketImpl = env->GetFieldID(clazz, "impl", "Ljava/net/SocketImpl;"); + LOG_FATAL_IF(gSocketOffsets.mSocketImpl == NULL, + "Unable to find impl field in java.net.Socket"); + + clazz = env->FindClass("java/net/SocketImpl"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.net.SocketImpl"); + gSocketImplOffsets.mFileDescriptor = env->GetFieldID(clazz, "fd", "Ljava/io/FileDescriptor;"); + LOG_FATAL_IF(gSocketImplOffsets.mFileDescriptor == NULL, + "Unable to find fd field in java.net.SocketImpl"); + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V"); + gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + clazz = env->FindClass(kParcelFileDescriptorPathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); + + return AndroidRuntime::registerNativeMethods( + env, kParcelFileDescriptorPathName, + gParcelFileDescriptorMethods, NELEM(gParcelFileDescriptorMethods)); +} + +} diff --git a/core/jni/android_os_Power.cpp b/core/jni/android_os_Power.cpp new file mode 100644 index 0000000..02a4083 --- /dev/null +++ b/core/jni/android_os_Power.cpp @@ -0,0 +1,123 @@ +/* //device/libs/android_runtime/android_os_Power.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "jni.h" +#include "android_runtime/AndroidRuntime.h" +#include <utils/misc.h> +#include <hardware/power.h> +#include <sys/reboot.h> + +namespace android +{ + +static void throw_NullPointerException(JNIEnv *env, const char* msg) +{ + jclass clazz; + clazz = env->FindClass("java/lang/NullPointerException"); + env->ThrowNew(clazz, msg); +} + +static void +acquireWakeLock(JNIEnv *env, jobject clazz, jint lock, jstring idObj) +{ + if (idObj == NULL) { + throw_NullPointerException(env, "id is null"); + return ; + } + + const char *id = env->GetStringUTFChars(idObj, NULL); + + acquire_wake_lock(lock, id); + + env->ReleaseStringUTFChars(idObj, id); +} + +static void +releaseWakeLock(JNIEnv *env, jobject clazz, jstring idObj) +{ + if (idObj == NULL) { + throw_NullPointerException(env, "id is null"); + return ; + } + + const char *id = env->GetStringUTFChars(idObj, NULL); + + release_wake_lock(id); + + env->ReleaseStringUTFChars(idObj, id); + +} + +static int +setLastUserActivityTimeout(JNIEnv *env, jobject clazz, jlong timeMS) +{ + return set_last_user_activity_timeout(timeMS/1000); +} + +static int +setLightBrightness(JNIEnv *env, jobject clazz, jint mask, jint brightness) +{ + return set_light_brightness(mask, brightness); +} + +static int +setScreenState(JNIEnv *env, jobject clazz, jboolean on) +{ + return set_screen_state(on); +} + +static void android_os_Power_shutdown(JNIEnv *env, jobject clazz) +{ + sync(); +#ifdef HAVE_ANDROID_OS + reboot(RB_POWER_OFF); +#endif +} + +static void android_os_Power_reboot(JNIEnv *env, jobject clazz, jstring reason) +{ + sync(); +#ifdef HAVE_ANDROID_OS + if (reason == NULL) { + reboot(RB_AUTOBOOT); + } else { + const char *chars = env->GetStringUTFChars(reason, NULL); + __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_RESTART2, (char*) chars); + env->ReleaseStringUTFChars(reason, chars); // In case it fails. + } +#endif +} + +static JNINativeMethod method_table[] = { + { "acquireWakeLock", "(ILjava/lang/String;)V", (void*)acquireWakeLock }, + { "releaseWakeLock", "(Ljava/lang/String;)V", (void*)releaseWakeLock }, + { "setLastUserActivityTimeout", "(J)I", (void*)setLastUserActivityTimeout }, + { "setLightBrightness", "(II)I", (void*)setLightBrightness }, + { "setScreenState", "(Z)I", (void*)setScreenState }, + { "shutdown", "()V", (void*)android_os_Power_shutdown }, + { "reboot", "(Ljava/lang/String;)V", (void*)android_os_Power_reboot }, +}; + +int register_android_os_Power(JNIEnv *env) +{ + return AndroidRuntime::registerNativeMethods( + env, "android/os/Power", + method_table, NELEM(method_table)); +} + +}; diff --git a/core/jni/android_os_StatFs.cpp b/core/jni/android_os_StatFs.cpp new file mode 100644 index 0000000..c658aa5 --- /dev/null +++ b/core/jni/android_os_StatFs.cpp @@ -0,0 +1,163 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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. + */ + +#if INCLUDE_SYS_MOUNT_FOR_STATFS +#include <sys/mount.h> +#else +#include <sys/statfs.h> +#endif + +#include <errno.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + + +namespace android +{ + +// ---------------------------------------------------------------------------- + +struct fields_t { + jfieldID context; +}; +static fields_t fields; + +// ---------------------------------------------------------------------------- + +static jint +android_os_StatFs_getBlockSize(JNIEnv *env, jobject thiz) +{ + struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context); + return stat->f_bsize; +} + +static jint +android_os_StatFs_getBlockCount(JNIEnv *env, jobject thiz) +{ + struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context); + return stat->f_blocks; +} + +static jint +android_os_StatFs_getFreeBlocks(JNIEnv *env, jobject thiz) +{ + struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context); + return stat->f_bfree; +} + +static jint +android_os_StatFs_getAvailableBlocks(JNIEnv *env, jobject thiz) +{ + struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context); + return stat->f_bavail; +} + +static void +android_os_StatFs_native_restat(JNIEnv *env, jobject thiz, jstring path) +{ + if (path == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + // get the object handle + struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context); + if (stat == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldException", NULL); + return; + } + + const char* pathstr = env->GetStringUTFChars(path, NULL); + if (pathstr == NULL) { + jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); + return; + } + + // note that stat will contain the new file data corresponding to + // pathstr + if (statfs(pathstr, stat) != 0) { + LOGE("statfs %s failed, errno: %d", pathstr, errno); + delete stat; + env->SetIntField(thiz, fields.context, 0); + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + } + // Release pathstr + env->ReleaseStringUTFChars(path, pathstr); +} + +static void +android_os_StatFs_native_setup(JNIEnv *env, jobject thiz, jstring path) +{ + if (path == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + struct statfs* stat = new struct statfs; + if (stat == NULL) { + jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); + return; + } + env->SetIntField(thiz, fields.context, (int)stat); + android_os_StatFs_native_restat(env, thiz, path); +} + +static void +android_os_StatFs_native_finalize(JNIEnv *env, jobject thiz) +{ + struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context); + if (stat != NULL) { + delete stat; + env->SetIntField(thiz, fields.context, 0); + } +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gMethods[] = { + {"getBlockSize", "()I", (void *)android_os_StatFs_getBlockSize}, + {"getBlockCount", "()I", (void *)android_os_StatFs_getBlockCount}, + {"getFreeBlocks", "()I", (void *)android_os_StatFs_getFreeBlocks}, + {"getAvailableBlocks", "()I", (void *)android_os_StatFs_getAvailableBlocks}, + {"native_setup", "(Ljava/lang/String;)V", (void *)android_os_StatFs_native_setup}, + {"native_finalize", "()V", (void *)android_os_StatFs_native_finalize}, + {"native_restat", "(Ljava/lang/String;)V", (void *)android_os_StatFs_native_restat}, +}; + + +int register_android_os_StatFs(JNIEnv *env) +{ + jclass clazz; + + clazz = env->FindClass("android/os/StatFs"); + if (clazz == NULL) { + LOGE("Can't find android/os/StatFs"); + return -1; + } + + fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (fields.context == NULL) { + LOGE("Can't find StatFs.mNativeContext"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + "android/os/StatFs", gMethods, NELEM(gMethods)); +} + +} // namespace android diff --git a/core/jni/android_os_SystemClock.cpp b/core/jni/android_os_SystemClock.cpp new file mode 100644 index 0000000..ffd0c1e --- /dev/null +++ b/core/jni/android_os_SystemClock.cpp @@ -0,0 +1,103 @@ +/* + * 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. + */ + + +/* + * System clock functions. + */ + +#include "JNIHelp.h" +#include "jni.h" +#include "android_runtime/AndroidRuntime.h" + +#include "utils/SystemClock.h" + +#include <sys/time.h> +#include <time.h> + +namespace android { + +/* + * native public static void setCurrentTimeMillis(long millis) + * + * Set the current time. This only works when running as root. + */ +static jboolean android_os_SystemClock_setCurrentTimeMillis(JNIEnv* env, + jobject clazz, jlong millis) +{ + return (setCurrentTimeMillis(millis) == 0); +} + +/* + * native public static long uptimeMillis(); + */ +static jlong android_os_SystemClock_uptimeMillis(JNIEnv* env, + jobject clazz) +{ + return (jlong)uptimeMillis(); +} + +/* + * native public static long elapsedRealtime(); + */ +static jlong android_os_SystemClock_elapsedRealtime(JNIEnv* env, + jobject clazz) +{ + return (jlong)elapsedRealtime(); +} + +/* + * native public static long currentThreadTimeMillis(); + */ +static jlong android_os_SystemClock_currentThreadTimeMillis(JNIEnv* env, + jobject clazz) +{ +#if defined(HAVE_POSIX_CLOCKS) + struct timespec tm; + + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm); + + return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000; +#else + struct timeval tv; + + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000LL + tv.tv_usec / 1000; +#endif +} + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "setCurrentTimeMillis", "(J)Z", + (void*) android_os_SystemClock_setCurrentTimeMillis }, + { "uptimeMillis", "()J", + (void*) android_os_SystemClock_uptimeMillis }, + { "elapsedRealtime", "()J", + (void*) android_os_SystemClock_elapsedRealtime }, + { "currentThreadTimeMillis", "()J", + (void*) android_os_SystemClock_currentThreadTimeMillis }, +}; +int register_android_os_SystemClock(JNIEnv* env) +{ + return AndroidRuntime::registerNativeMethods(env, + "android/os/SystemClock", gMethods, NELEM(gMethods)); +} + +}; // namespace android + diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp new file mode 100644 index 0000000..ca4fa11 --- /dev/null +++ b/core/jni/android_os_SystemProperties.cpp @@ -0,0 +1,108 @@ +/* //device/libs/android_runtime/android_os_SystemProperties.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "cutils/properties.h" +#include "jni.h" +#include "android_runtime/AndroidRuntime.h" +#include <nativehelper/JNIHelp.h> + +namespace android +{ + +static jstring SystemProperties_getSS(JNIEnv *env, jobject clazz, + jstring keyJ, jstring defJ) +{ + int len; + const char* key; + char buf[PROPERTY_VALUE_MAX]; + jstring rvJ = NULL; + + if (keyJ == NULL) { + jniThrowException(env, "java/lang/NullPointerException", + "key must not be null."); + goto error; + } + + key = env->GetStringUTFChars(keyJ, NULL); + + len = property_get(key, buf, ""); + if ((len <= 0) && (defJ != NULL)) { + rvJ = defJ; + } else if (len >= 0) { + rvJ = env->NewStringUTF(buf); + } else { + rvJ = env->NewStringUTF(""); + } + + env->ReleaseStringUTFChars(keyJ, key); + +error: + return rvJ; +} + +static jstring SystemProperties_getS(JNIEnv *env, jobject clazz, + jstring keyJ) +{ + return SystemProperties_getSS(env, clazz, keyJ, NULL); +} + +static void SystemProperties_set(JNIEnv *env, jobject clazz, + jstring keyJ, jstring valJ) +{ + int err; + const char* key; + const char* val; + + if (keyJ == NULL) { + jniThrowException(env, "java/lang/NullPointerException", + "key must not be null."); + return ; + } + key = env->GetStringUTFChars(keyJ, NULL); + + if (valJ == NULL) { + val = ""; /* NULL pointer not allowed here */ + } else { + val = env->GetStringUTFChars(valJ, NULL); + } + + err = property_set(key, val); + + env->ReleaseStringUTFChars(keyJ, key); + + if (valJ != NULL) { + env->ReleaseStringUTFChars(valJ, val); + } +} + +static JNINativeMethod method_table[] = { + { "native_get", "(Ljava/lang/String;)Ljava/lang/String;", + (void*) SystemProperties_getS }, + { "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + (void*) SystemProperties_getSS }, + { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V", + (void*) SystemProperties_set }, +}; + +int register_android_os_SystemProperties(JNIEnv *env) +{ + return AndroidRuntime::registerNativeMethods( + env, "android/os/SystemProperties", + method_table, NELEM(method_table)); +} + +}; diff --git a/core/jni/android_os_UEventObserver.cpp b/core/jni/android_os_UEventObserver.cpp new file mode 100644 index 0000000..cac4372 --- /dev/null +++ b/core/jni/android_os_UEventObserver.cpp @@ -0,0 +1,70 @@ +/* + * 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_TAG "UEventObserver" +#include "utils/Log.h" + +#include "hardware/uevent.h" +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +namespace android +{ + +static void +android_os_UEventObserver_native_setup(JNIEnv *env, jclass clazz) +{ + if (!uevent_init()) { + jniThrowException(env, "java/lang/RuntimeException", + "Unable to open socket for UEventObserver"); + } +} + +static int +android_os_UEventObserver_next_event(JNIEnv *env, jclass clazz, jbyteArray jbuffer) +{ + int buf_sz = env->GetArrayLength(jbuffer); + char *buffer = (char*)env->GetByteArrayElements(jbuffer, NULL); + + int length = uevent_next_event(buffer, buf_sz - 1); + + env->ReleaseByteArrayElements(jbuffer, (jbyte*)buffer, 0); + + return length; +} + +static JNINativeMethod gMethods[] = { + {"native_setup", "()V", (void *)android_os_UEventObserver_native_setup}, + {"next_event", "([B)I", (void *)android_os_UEventObserver_next_event}, +}; + + +int register_android_os_UEventObserver(JNIEnv *env) +{ + jclass clazz; + + clazz = env->FindClass("android/os/UEventObserver"); + if (clazz == NULL) { + LOGE("Can't find android/os/UEventObserver"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + "android/os/UEventObserver", gMethods, NELEM(gMethods)); +} + +} // namespace android diff --git a/core/jni/android_pim_EventRecurrence.cpp b/core/jni/android_pim_EventRecurrence.cpp new file mode 100644 index 0000000..cbe99bc --- /dev/null +++ b/core/jni/android_pim_EventRecurrence.cpp @@ -0,0 +1,199 @@ +/* //device/libs/android_runtime/android_pim_EventRecurrence.cpp
+**
+** Copyright 2006, The Android Open Source Project +**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <pim/EventRecurrence.h>
+#include "jni.h"
+#include "nativehelper/JNIHelp.h"
+#include <utils/String8.h>
+
+namespace android {
+
+struct cached_array_fields_t
+{
+ jfieldID array;
+ jfieldID count;
+};
+
+static jclass clazz;
+static jfieldID freq_field;
+static jfieldID until_field;
+static jfieldID count_field;
+static jfieldID interval_field;
+static jfieldID wkst_field;
+static cached_array_fields_t bysecond_fields;
+static cached_array_fields_t byminute_fields;
+static cached_array_fields_t byhour_fields;
+static cached_array_fields_t byday_fields;
+static cached_array_fields_t bydayNum_fields;
+static cached_array_fields_t bymonthday_fields;
+static cached_array_fields_t byyearday_fields;
+static cached_array_fields_t byweekno_fields;
+static cached_array_fields_t bymonth_fields;
+static cached_array_fields_t bysetpos_fields;
+
+static status_t
+set_array(JNIEnv* env, int inCount, int* inArray,
+ jobject This, const cached_array_fields_t& fields)
+{
+ if (inCount > 0) {
+ jintArray array = (jintArray) env->GetObjectField(This, fields.array);
+ if (array == NULL || env->GetArrayLength(array) < inCount) {
+ // +4 because it's cheap to allocate a little extra here, and
+ // that reduces the chance that we'll come back here again
+ array = env->NewIntArray(inCount+4);
+ env->SetObjectField(This, fields.array, array);
+ }
+ if (array == NULL) {
+ return NO_MEMORY;
+ }
+ env->SetIntArrayRegion(array, 0, inCount, inArray);
+
+ }
+ env->SetIntField(This, fields.count, inCount);
+ return NO_ERROR;
+}
+
+/*
+ * In class android.pim.EventRecurrence
+ * public native int parse(String str);
+ */
+#define SET_ARRAY_AND_CHECK(name) \
+ /*printf("setting " #name " to %d elements\n", er.name##Count);*/ \
+ if (set_array(env, er.name##Count, er.name, This, name##_fields) \
+ != NO_ERROR) { \
+ jniThrowException(env, "java/lang/RuntimeException", \
+ "EventRecurrence.parse error setting field " #name " or " \
+ #name "Count."); \
+ return ; \
+ }
+static void
+EventRecurrence_parse(JNIEnv* env, jobject This, jstring jstr)
+{
+ if (jstr == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "EventRecurrence.parse str parameter null");
+ return ;
+ }
+ jboolean isCopy;
+ const jchar* jchars = env->GetStringChars(jstr, &isCopy);
+ jsize len = env->GetStringLength(jstr);
+ String16 str(jchars, len);
+ env->ReleaseStringChars(jstr, jchars);
+
+ //printf("the string was '%s'\n", String8(str).string());
+
+ EventRecurrence er;
+ if (NO_ERROR != er.parse(str)) {
+ String8 msg("Error parsing recurrence: '");
+ msg.append(String8(str));
+ msg.append("'");
+
+ jniThrowException(env,
+ "android/pim/EventRecurrence$InvalidFormatException",
+ msg.string());
+ return ;
+ }
+
+ jstring untilStr;
+ if (er.until.size() > 0) {
+ untilStr = env->NewString(er.until.string(), er.until.size());
+ if (untilStr == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "EventRecurrence.parse error setting field 'until'");
+ return ;
+ }
+ } else {
+ untilStr = NULL;
+ }
+ env->SetObjectField(This, until_field, untilStr);
+
+ env->SetIntField(This, freq_field, er.freq);
+ env->SetIntField(This, count_field, er.count);
+ env->SetIntField(This, interval_field, er.interval);
+ env->SetIntField(This, wkst_field, er.wkst);
+
+ SET_ARRAY_AND_CHECK(bysecond)
+ SET_ARRAY_AND_CHECK(byminute)
+ SET_ARRAY_AND_CHECK(byhour)
+ SET_ARRAY_AND_CHECK(byday)
+ // we'll just set the bydayCount field twice, it'll be less code total
+ if (set_array(env, er.bydayCount, er.bydayNum, This, bydayNum_fields)
+ != NO_ERROR) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "EventRecurrence.parse error setting field bydayNum or "
+ "bydayCount.");
+ return ;
+ }
+ SET_ARRAY_AND_CHECK(bymonthday)
+ SET_ARRAY_AND_CHECK(byyearday)
+ SET_ARRAY_AND_CHECK(byweekno)
+ SET_ARRAY_AND_CHECK(bymonth)
+ SET_ARRAY_AND_CHECK(bysetpos)
+}
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod METHODS[] = {
+ /* name, signature, funcPtr */
+ { "parse", "(Ljava/lang/String;)V", (void*)EventRecurrence_parse }
+};
+
+static const char*const CLASS_NAME = "android/pim/EventRecurrence";
+
+int register_android_pim_EventRecurrence(JNIEnv* env)
+{
+ clazz = env->FindClass(CLASS_NAME);
+ if (clazz == NULL) {
+ LOGE("Field lookup unable to find class '%s'\n", CLASS_NAME);
+ return -1;
+ }
+
+ freq_field = env->GetFieldID(clazz, "freq", "I");
+ count_field = env->GetFieldID(clazz, "count", "I");
+ interval_field = env->GetFieldID(clazz, "interval", "I");
+ wkst_field = env->GetFieldID(clazz, "wkst", "I");
+
+ until_field = env->GetFieldID(clazz, "until", "Ljava/lang/String;");
+
+ bysecond_fields.array = env->GetFieldID(clazz, "bysecond", "[I");
+ bysecond_fields.count = env->GetFieldID(clazz, "bysecondCount", "I");
+ byminute_fields.array = env->GetFieldID(clazz, "byminute", "[I");
+ byminute_fields.count = env->GetFieldID(clazz, "byminuteCount", "I");
+ byhour_fields.array = env->GetFieldID(clazz, "byhour", "[I");
+ byhour_fields.count = env->GetFieldID(clazz, "byhourCount", "I");
+ byday_fields.array = env->GetFieldID(clazz, "byday", "[I");
+ byday_fields.count = env->GetFieldID(clazz, "bydayCount", "I");
+ bydayNum_fields.array = env->GetFieldID(clazz, "bydayNum", "[I");
+ bydayNum_fields.count = byday_fields.count;
+ bymonthday_fields.array = env->GetFieldID(clazz, "bymonthday", "[I");
+ bymonthday_fields.count = env->GetFieldID(clazz, "bymonthdayCount", "I");
+ byyearday_fields.array = env->GetFieldID(clazz, "byyearday", "[I");
+ byyearday_fields.count = env->GetFieldID(clazz, "byyeardayCount", "I");
+ byweekno_fields.array = env->GetFieldID(clazz, "byweekno", "[I");
+ byweekno_fields.count = env->GetFieldID(clazz, "byweeknoCount", "I");
+ bymonth_fields.array = env->GetFieldID(clazz, "bymonth", "[I");
+ bymonth_fields.count = env->GetFieldID(clazz, "bymonthCount", "I");
+ bysetpos_fields.array = env->GetFieldID(clazz, "bysetpos", "[I");
+ bysetpos_fields.count = env->GetFieldID(clazz, "bysetposCount", "I");
+
+ return jniRegisterNativeMethods(env, CLASS_NAME,
+ METHODS, sizeof(METHODS)/sizeof(METHODS[0]));
+}
+
+}; // namespace android
+
diff --git a/core/jni/android_pim_Time.cpp b/core/jni/android_pim_Time.cpp new file mode 100644 index 0000000..c1dd499 --- /dev/null +++ b/core/jni/android_pim_Time.cpp @@ -0,0 +1,594 @@ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "Log_println" + +#include <utils/Log.h> +#include <utils/String8.h> +#include <assert.h> + +#include "jni.h" +#include "utils/misc.h" +#include "android_runtime/AndroidRuntime.h" +#include <utils/TimeUtils.h> +#include <nativehelper/JNIHelp.h> + +namespace android { + +static jfieldID g_allDayField = 0; +static jfieldID g_secField = 0; +static jfieldID g_minField = 0; +static jfieldID g_hourField = 0; +static jfieldID g_mdayField = 0; +static jfieldID g_monField = 0; +static jfieldID g_yearField = 0; +static jfieldID g_wdayField = 0; +static jfieldID g_ydayField = 0; +static jfieldID g_isdstField = 0; +static jfieldID g_gmtoffField = 0; +static jfieldID g_timezoneField = 0; + +static inline bool java2time(JNIEnv* env, Time* t, jobject o) +{ + t->t.tm_sec = env->GetIntField(o, g_secField); + t->t.tm_min = env->GetIntField(o, g_minField); + t->t.tm_hour = env->GetIntField(o, g_hourField); + t->t.tm_mday = env->GetIntField(o, g_mdayField); + t->t.tm_mon = env->GetIntField(o, g_monField); + t->t.tm_year = (env->GetIntField(o, g_yearField))-1900; + t->t.tm_wday = env->GetIntField(o, g_wdayField); + t->t.tm_yday = env->GetIntField(o, g_ydayField); + t->t.tm_isdst = env->GetIntField(o, g_isdstField); + t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField); + bool allDay = env->GetIntField(o, g_allDayField); + if (allDay && + ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) { + char msg[100]; + sprintf(msg, "allDay is true but sec, min, hour are not 0."); + jniThrowException(env, "java/lang/IllegalArgumentException", msg); + return false; + } + return true; +} + +static inline void time2java(JNIEnv* env, jobject o, const Time &t) +{ + env->SetIntField(o, g_secField, t.t.tm_sec); + env->SetIntField(o, g_minField, t.t.tm_min); + env->SetIntField(o, g_hourField, t.t.tm_hour); + env->SetIntField(o, g_mdayField, t.t.tm_mday); + env->SetIntField(o, g_monField, t.t.tm_mon); + env->SetIntField(o, g_yearField, t.t.tm_year+1900); + env->SetIntField(o, g_wdayField, t.t.tm_wday); + env->SetIntField(o, g_ydayField, t.t.tm_yday); + env->SetIntField(o, g_isdstField, t.t.tm_isdst); + env->SetLongField(o, g_gmtoffField, t.t.tm_gmtoff); +} + +#define ACQUIRE_TIMEZONE(This, t) \ + jstring timezoneString_##This \ + = (jstring) env->GetObjectField(This, g_timezoneField); \ + t.timezone = env->GetStringUTFChars(timezoneString_##This, NULL); + +#define RELEASE_TIMEZONE(This, t) \ + env->ReleaseStringUTFChars(timezoneString_##This, t.timezone); + + +// ============================================================================ + +static jlong android_pim_Time_normalize(JNIEnv* env, jobject This, + jboolean ignoreDst) +{ + Time t; + if (!java2time(env, &t, This)) return 0L; + ACQUIRE_TIMEZONE(This, t) + + int64_t result = t.toMillis(ignoreDst != 0); + + time2java(env, This, t); + RELEASE_TIMEZONE(This, t) + + return result; +} + +static void android_pim_Time_switchTimezone(JNIEnv* env, jobject This, + jstring timezoneObject) +{ + Time t; + if (!java2time(env, &t, This)) return; + ACQUIRE_TIMEZONE(This, t) + + const char* timezone = env->GetStringUTFChars(timezoneObject, NULL); + + t.switchTimezone(timezone); + + time2java(env, This, t); + env->ReleaseStringUTFChars(timezoneObject, timezone); + RELEASE_TIMEZONE(This, t) + + // we do this here because there's no point in reallocating the string + env->SetObjectField(This, g_timezoneField, timezoneObject); +} + +static jint android_pim_Time_compare(JNIEnv* env, jobject clazz, + jobject aObject, jobject bObject) +{ + Time a, b; + + if (!java2time(env, &a, aObject)) return 0; + ACQUIRE_TIMEZONE(aObject, a) + + if (!java2time(env, &b, bObject)) return 0; + ACQUIRE_TIMEZONE(bObject, b) + + int result = Time::compare(a, b); + + RELEASE_TIMEZONE(aObject, a) + RELEASE_TIMEZONE(bObject, b) + + return result; +} + +static jstring android_pim_Time_format2445(JNIEnv* env, jobject This) +{ + Time t; + if (!java2time(env, &t, This)) return env->NewStringUTF(""); + bool allDay = env->GetIntField(This, g_allDayField); + + if (!allDay) { + ACQUIRE_TIMEZONE(This, t) + bool inUtc = strcmp("UTC", t.timezone) == 0; + short buf[16]; + t.format2445(buf, true); + RELEASE_TIMEZONE(This, t) + if (inUtc) { + // The letter 'Z' is appended to the end so allow for one + // more character in the buffer. + return env->NewString((jchar*)buf, 16); + } else { + return env->NewString((jchar*)buf, 15); + } + } else { + short buf[8]; + t.format2445(buf, false); + return env->NewString((jchar*)buf, 8); + } +} + +static jstring android_pim_Time_format(JNIEnv* env, jobject This, + jstring formatObject) +{ + Time t; + if (!java2time(env, &t, This)) return env->NewStringUTF(""); + ACQUIRE_TIMEZONE(This, t) + + const char* format = env->GetStringUTFChars(formatObject, NULL); + + String8 r = t.format(format); + + env->ReleaseStringUTFChars(formatObject, format); + RELEASE_TIMEZONE(This, t) + + return env->NewStringUTF(r.string()); +} + + +static jstring android_pim_Time_toString(JNIEnv* env, jobject This) +{ + Time t; + if (!java2time(env, &t, This)) return env->NewStringUTF("");; + ACQUIRE_TIMEZONE(This, t) + + String8 r = t.toString(); + + RELEASE_TIMEZONE(This, t) + + return env->NewStringUTF(r.string()); +} + +static void android_pim_Time_setToNow(JNIEnv* env, jobject This) +{ + env->SetBooleanField(This, g_allDayField, JNI_FALSE); + Time t; + ACQUIRE_TIMEZONE(This, t) + + t.setToNow(); + + time2java(env, This, t); + RELEASE_TIMEZONE(This, t) +} + +static jlong android_pim_Time_toMillis(JNIEnv* env, jobject This, + jboolean ignoreDst) +{ + Time t; + if (!java2time(env, &t, This)) return 0L; + ACQUIRE_TIMEZONE(This, t) + + int64_t result = t.toMillis(ignoreDst != 0); + + RELEASE_TIMEZONE(This, t) + + return result; +} + +static void android_pim_Time_set(JNIEnv* env, jobject This, jlong millis) +{ + env->SetBooleanField(This, g_allDayField, JNI_FALSE); + Time t; + if (!java2time(env, &t, This)) return; + ACQUIRE_TIMEZONE(This, t) + + t.set(millis); + + time2java(env, This, t); + RELEASE_TIMEZONE(This, t) +} + + +// ============================================================================ +// Just do this here because it's not worth recreating the strings + +static int get_char(JNIEnv* env, const jchar *s, int spos, int mul, + bool *thrown) +{ + jchar c = s[spos]; + if (c >= '0' && c <= '9') { + return (c - '0') * mul; + } else { + char msg[100]; + sprintf(msg, "Parse error at pos=%d", spos); + jniThrowException(env, "android/util/TimeFormatException", msg); + *thrown = true; + return 0; + } +} + +static bool check_char(JNIEnv* env, const jchar *s, int spos, jchar expected) +{ + jchar c = s[spos]; + if (c != expected) { + char msg[100]; + sprintf(msg, "Unexpected %c at pos=%d. Expected %c.", c, spos, + expected); + jniThrowException(env, "android/util/TimeFormatException", msg); + return false; + } + return true; +} + + +static void android_pim_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; + + 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; + env->SetIntField(This, g_yearField, n); + + n = get_char(env, s, 4, 10, &thrown); + n += get_char(env, s, 5, 1, &thrown); + n--; + if (thrown) return; + env->SetIntField(This, g_monField, n); + + n = get_char(env, s, 6, 10, &thrown); + n += get_char(env, s, 7, 1, &thrown); + if (thrown) return; + env->SetIntField(This, g_mdayField, n); + + if (len >= 15) { + env->SetBooleanField(This, g_allDayField, JNI_FALSE); + n = get_char(env, s, 9, 10, &thrown); + n += get_char(env, s, 10, 1, &thrown); + if (thrown) return; + env->SetIntField(This, g_hourField, n); + + n = get_char(env, s, 11, 10, &thrown); + n += get_char(env, s, 12, 1, &thrown); + if (thrown) return; + env->SetIntField(This, g_minField, n); + + n = get_char(env, s, 13, 10, &thrown); + n += get_char(env, s, 14, 1, &thrown); + if (thrown) return; + env->SetIntField(This, g_secField, n); + } else { + 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); +} + +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, + jobject This, + jstring strObj) +{ + jsize len = env->GetStringLength(strObj); + const jchar *s = env->GetStringChars(strObj, NULL); + + bool thrown = false; + int n; + jboolean inUtc = 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); + + // - + if (!check_char(env, s, 4, '-')) return false; + + // month + n = get_char(env, s, 5, 10, &thrown); + n += get_char(env, s, 6, 1, &thrown); + --n; + if (thrown) return false; + env->SetIntField(This, g_monField, n); + + // - + if (!check_char(env, s, 7, '-')) return false; + + // day + n = get_char(env, s, 8, 10, &thrown); + n += get_char(env, s, 9, 1, &thrown); + if (thrown) return false; + env->SetIntField(This, g_mdayField, n); + + if (len >= 17) { + // T + if (!check_char(env, s, 10, 'T')) return false; + + env->SetBooleanField(This, g_allDayField, JNI_FALSE); + // hour + n = get_char(env, s, 11, 10, &thrown); + n += get_char(env, s, 12, 1, &thrown); + if (thrown) return false; + int hour = n; + // env->SetIntField(This, g_hourField, n); + + // : + if (!check_char(env, s, 13, ':')) return false; + + // minute + n = get_char(env, s, 14, 10, &thrown); + n += get_char(env, s, 15, 1, &thrown); + if (thrown) return false; + int minute = n; + // env->SetIntField(This, g_minField, n); + + // : + if (!check_char(env, s, 16, ':')) return false; + + // second + n = get_char(env, s, 17, 10, &thrown); + n += get_char(env, s, 18, 1, &thrown); + if (thrown) return false; + env->SetIntField(This, g_secField, n); + + // skip the '.XYZ' -- we don't care about subsecond precision. + int offset = 0; + if (len >= 23) { + char c = s[23]; + + // NOTE: the offset is meant to be subtracted to get from local time + // to UTC. we therefore use 1 for '-' and -1 for '+'. + switch (c) { + case 'Z': + // Zulu time -- UTC + offset = 0; + break; + case '-': + offset = 1; + break; + case '+': + offset = -1; + break; + default: + char msg[100]; + sprintf(msg, "Unexpected %c at position 19. Expected + or -", + c); + jniThrowException(env, "android/util/TimeFormatException", msg); + return false; + } + inUtc = true; + + if (offset != 0) { + // hour + n = get_char(env, s, 24, 10, &thrown); + n += get_char(env, s, 25, 1, &thrown); + if (thrown) return false; + n *= offset; + hour += n; + + // : + if (!check_char(env, s, 26, ':')) return false; + + // minute + n = get_char(env, s, 27, 10, &thrown); + n += get_char(env, s, 28, 1, &thrown); + if (thrown) return false; + n *= offset; + minute += n; + } + } + env->SetIntField(This, g_hourField, hour); + env->SetIntField(This, g_minField, minute); + + if (offset != 0) { + // we need to normalize after applying the hour and minute offsets + android_pim_Time_normalize(env, This, false /* use isdst */); + // The timezone is set to UTC in the calling Java code. + } + } else { + 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; +} + +// ============================================================================ +/* + * JNI registration. + */ +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 } +}; + +int register_android_pim_Time(JNIEnv* env) +{ + jclass timeClass = env->FindClass("android/pim/Time"); + + g_allDayField = env->GetFieldID(timeClass, "allDay", "Z"); + g_secField = env->GetFieldID(timeClass, "second", "I"); + g_minField = env->GetFieldID(timeClass, "minute", "I"); + g_hourField = env->GetFieldID(timeClass, "hour", "I"); + g_mdayField = env->GetFieldID(timeClass, "monthDay", "I"); + g_monField = env->GetFieldID(timeClass, "month", "I"); + g_yearField = env->GetFieldID(timeClass, "year", "I"); + g_wdayField = env->GetFieldID(timeClass, "weekDay", "I"); + g_ydayField = env->GetFieldID(timeClass, "yearDay", "I"); + g_isdstField = env->GetFieldID(timeClass, "isDst", "I"); + 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)); +} + +}; // namespace android + diff --git a/core/jni/android_security_Md5MessageDigest.cpp b/core/jni/android_security_Md5MessageDigest.cpp new file mode 100644 index 0000000..3533559 --- /dev/null +++ b/core/jni/android_security_Md5MessageDigest.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jni.h" +#include <JNIHelp.h> +#include <stdlib.h> +#include <stdint.h> + +#include <openssl/md5.h> + +namespace android +{ + +struct fields_t { + jfieldID context; +}; +static fields_t fields; + +static void native_init(JNIEnv *env, jobject clazz) +{ + MD5_CTX* context = (MD5_CTX *)malloc(sizeof(MD5_CTX)); + MD5_Init(context); + + env->SetIntField(clazz, fields.context, (int)context); +} + +static void native_reset(JNIEnv *env, jobject clazz) +{ + MD5_CTX *context = (MD5_CTX *)env->GetIntField(clazz, fields.context); + if (context != NULL) { + free(context); + env->SetIntField(clazz, fields.context, 0 ); + } +} + +static void native_update(JNIEnv *env, jobject clazz, jbyteArray dataArray) +{ + jbyte * data; + jsize dataSize; + MD5_CTX *context = (MD5_CTX *)env->GetIntField(clazz, fields.context); + + if (context == NULL) { + native_init(env, clazz); + context = (MD5_CTX *)env->GetIntField(clazz, fields.context); + } + + data = env->GetByteArrayElements(dataArray, NULL); + if (data == NULL) { + LOGE("Unable to get byte array elements"); + jniThrowException(env, "java/lang/IllegalArgumentException", + "Invalid data array when calling MessageDigest.update()"); + return; + } + dataSize = env->GetArrayLength(dataArray); + + MD5_Update(context, data, dataSize); + + env->ReleaseByteArrayElements(dataArray, data, 0); +} + +static jbyteArray native_digest(JNIEnv *env, jobject clazz) +{ + jbyteArray array; + jbyte md[MD5_DIGEST_LENGTH]; + MD5_CTX *context = (MD5_CTX *)env->GetIntField(clazz, fields.context); + + MD5_Final((uint8_t*)md, context); + + array = env->NewByteArray(MD5_DIGEST_LENGTH); + LOG_ASSERT(array, "Native could not create new byte[]"); + + env->SetByteArrayRegion(array, 0, MD5_DIGEST_LENGTH, md); + + native_reset(env, clazz); + + return array; +} + + +/* + * JNI registration. + */ + +static JNINativeMethod gMethods[] = +{ + /* name, signature, funcPtr */ + {"init", "()V", (void *)native_init}, + {"update", "([B)V", (void *)native_update}, + {"digest", "()[B", (void *)native_digest}, + {"reset", "()V", (void *)native_reset}, +}; + +int register_android_security_Md5MessageDigest(JNIEnv *env) +{ + jclass clazz; + + clazz = env->FindClass("android/security/Md5MessageDigest"); + if (clazz == NULL) { + LOGE("Can't find android/security/Md5MessageDigest"); + return -1; + } + + fields.context = env->GetFieldID(clazz, "mNativeMd5Context", "I"); + if (fields.context == NULL) { + LOGE("Can't find Md5MessageDigest.mNativeMd5Context"); + return -1; + } + + return jniRegisterNativeMethods(env, "android/security/Md5MessageDigest", + gMethods, NELEM(gMethods)); +} + +}; diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp new file mode 100644 index 0000000..3ff6af1 --- /dev/null +++ b/core/jni/android_server_BluetoothDeviceService.cpp @@ -0,0 +1,1144 @@ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Adapter" +#define LOG_TAG "BluetoothDeviceService.cpp" + +#include "android_bluetooth_common.h" +#include "android_runtime/AndroidRuntime.h" +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> + +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#ifdef HAVE_BLUETOOTH +#include <dbus/dbus.h> +#include <bluedroid/bluetooth.h> +#endif + +#include <cutils/properties.h> + +namespace android { + +#ifdef HAVE_BLUETOOTH +// We initialize these variables when we load class +// android.server.BluetoothDeviceService +static jfieldID field_mNativeData; + +typedef struct { + JNIEnv *env; + DBusConnection *conn; + const char *adapter; // dbus object name of the local adapter +} native_data_t; + +void onCreateBondingResult(DBusMessage *msg, void *user); +void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user); + +/** Get native data stored in the opaque (Java code maintained) pointer mNativeData + * Perform quick sanity check, if there are any problems return NULL + */ +static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { + native_data_t *nat = + (native_data_t *)(env->GetIntField(object, field_mNativeData)); + if (nat == NULL || nat->conn == NULL) { + LOGE("Uninitialized native data\n"); + return NULL; + } + return nat; +} +#endif + +static void classInitNative(JNIEnv* env, jclass clazz) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + field_mNativeData = get_field(env, clazz, "mNativeData", "I"); +#endif +} + +/* Returns true on success (even if adapter is present but disabled). + * Return false if dbus is down, or another serious error (out of memory) +*/ +static bool initializeNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t)); + if (NULL == nat) { + LOGE("%s: out of memory!", __FUNCTION__); + return false; + } + nat->env = env; + + env->SetIntField(object, field_mNativeData, (jint)nat); + DBusError err; + dbus_error_init(&err); + dbus_threads_init_default(); + nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (dbus_error_is_set(&err)) { + LOGE("Could not get onto the system bus: %s", err.message); + dbus_error_free(&err); + return false; + } + + nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME; +#endif /*HAVE_BLUETOOTH*/ + return true; +} + +static void cleanupNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = + (native_data_t *)env->GetIntField(object, field_mNativeData); + if (nat) { + free(nat); + nat = NULL; + } +#endif +} + +static jstring getNameNative(JNIEnv *env, jobject object){ + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "GetName", + DBUS_TYPE_INVALID); + return reply ? dbus_returns_string(env, reply) : NULL; + } +#endif + return NULL; +} + +static jstring getAdapterPathNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + return (env->NewStringUTF(nat->adapter)); + } +#endif + return NULL; +} + + +static jboolean startDiscoveryNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + DBusError err; + const char *name; + jboolean ret = JNI_FALSE; + + native_data_t *nat = get_native_data(env, object); + if (nat == NULL) { + goto done; + } + + dbus_error_init(&err); + + /* Compose the command */ + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, + DBUS_CLASS_NAME, "DiscoverDevices"); + + if (msg == NULL) { + LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__); + goto done; + } + + /* Send the command. */ + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + if (dbus_error_is_set(&err)) { + /* We treat the in-progress error code as success. */ + if(strcmp(err.message, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { + LOGW("%s: D-Bus error: %s, treating as startDiscoveryNative success\n", + __FUNCTION__, err.message); + ret = JNI_TRUE; + goto done; + } else { + LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); + ret = JNI_FALSE; + goto done; + } + } + + ret = JNI_TRUE; +done: + if (reply) dbus_message_unref(reply); + if (msg) dbus_message_unref(msg); + return ret; +#else + return JNI_FALSE; +#endif +} + +static void cancelDiscoveryNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + DBusError err; + const char *name; + jstring ret; + native_data_t *nat; + + dbus_error_init(&err); + + nat = get_native_data(env, object); + if (nat == NULL) { + goto done; + } + + /* Compose the command */ + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, + DBUS_CLASS_NAME, "CancelDiscovery"); + + if (msg == NULL) { + LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__); + goto done; + } + + /* Send the command. */ + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + if (dbus_error_is_set(&err)) { + if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized") == 0) { + // hcid sends this if there is no active discovery to cancel + LOGV("%s: There was no active discovery to cancel", __FUNCTION__); + dbus_error_free(&err); + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + } + } + +done: + if (msg) dbus_message_unref(msg); + if (reply) dbus_message_unref(reply); +#endif +} + +static jboolean startPeriodicDiscoveryNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + DBusError err; + jboolean ret = JNI_FALSE; + + native_data_t *nat = get_native_data(env, object); + if (nat == NULL) { + goto done; + } + + dbus_error_init(&err); + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, + DBUS_CLASS_NAME, "StartPeriodicDiscovery"); + if (msg == NULL) { + LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__); + goto done; + } + + /* Send the command. */ + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + if (dbus_error_is_set(&err)) { + /* We treat the in-progress error code as success. */ + if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { + LOGW("%s: D-Bus error: %s (%s), treating as " + "startPeriodicDiscoveryNative success\n", + __FUNCTION__, err.name, err.message); + } else { + LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); + ret = JNI_FALSE; + goto done; + } + } + + ret = JNI_TRUE; +done: + if (reply) dbus_message_unref(reply); + if (msg) dbus_message_unref(msg); + return ret; +#else + return JNI_FALSE; +#endif +} + +static jboolean stopPeriodicDiscoveryNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + DBusError err; + const char *name; + jboolean ret = JNI_FALSE; + + native_data_t *nat = get_native_data(env, object); + if (nat == NULL) { + goto done; + } + + dbus_error_init(&err); + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, + DBUS_CLASS_NAME, "StopPeriodicDiscovery"); + if (msg == NULL) { + LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__); + goto done; + } + + /* Send the command. */ + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + if (dbus_error_is_set(&err)) { + /* We treat the in-progress error code as success. */ + if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { + LOGW("%s: D-Bus error: %s (%s), treating as " + "stopPeriodicDiscoveryNative success\n", + __FUNCTION__, err.name, err.message); + } else { + LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); + ret = JNI_FALSE; + goto done; + } + } + + ret = JNI_TRUE; +done: + if (reply) dbus_message_unref(reply); + if (msg) dbus_message_unref(msg); + return ret; +#else + return JNI_FALSE; +#endif +} + +static jboolean isPeriodicDiscoveryNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "IsPeriodicDiscovery", + DBUS_TYPE_INVALID); + return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jboolean setDiscoverableTimeoutNative(JNIEnv *env, jobject object, jint timeout_s) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + + if (timeout_s < 0) { + return JNI_FALSE; + } + + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "SetDiscoverableTimeout", + DBUS_TYPE_UINT32, &timeout_s, + DBUS_TYPE_INVALID); + if (reply != NULL) { + dbus_message_unref(reply); + return JNI_TRUE; + } + } +#endif + return JNI_FALSE; +} + +static jint getDiscoverableTimeoutNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "GetDiscoverableTimeout", + DBUS_TYPE_INVALID); + return reply ? dbus_returns_uint32(env, reply) : -1; + } +#endif + return -1; +} + +static jboolean isConnectedNative(JNIEnv *env, jobject object, jstring address) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "IsConnected", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static void disconnectRemoteDeviceNative(JNIEnv *env, jobject object, jstring address) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + // Set a timeout of 5 seconds. Specifying the default timeout is + // not long enough, as a remote-device disconnect results in + // signal RemoteDisconnectRequested being sent, followed by a + // delay of 2 seconds, after which the actual disconnect takes + // place. + DBusMessage *reply = + dbus_func_args_timeout(env, nat->conn, 60000, nat->adapter, + DBUS_CLASS_NAME, "DisconnectRemoteDevice", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + if (reply) dbus_message_unref(reply); + } +#endif +} + +static jboolean isConnectableNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "IsConnectable", + DBUS_TYPE_INVALID); + return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jboolean isDiscoverableNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "IsDiscoverable", + DBUS_TYPE_INVALID); + return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jstring getModeNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "GetMode", + DBUS_TYPE_INVALID); + return reply ? dbus_returns_string(env, reply) : NULL; + } +#endif + return NULL; +} + +static jboolean setModeNative(JNIEnv *env, jobject object, jstring mode) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_mode = env->GetStringUTFChars(mode, NULL); + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "SetMode", + DBUS_TYPE_STRING, &c_mode, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(mode, c_mode); + if (reply) { + dbus_message_unref(reply); + return JNI_TRUE; + } + return JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static void common_Bonding(JNIEnv *env, jobject object, int timeout_ms, + const char *func, jstring address) { +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + LOGV("... address = %s", c_address); + DBusMessage *reply = + dbus_func_args_timeout(env, nat->conn, timeout_ms, nat->adapter, + DBUS_CLASS_NAME, func, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + if (reply) { + dbus_message_unref(reply); + } + } +#endif +} + +static jboolean createBondingNative(JNIEnv *env, jobject object, + jstring address, jint timeout_ms) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + LOGV("... address = %s", c_address); + char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); + strlcpy(context_address, c_address, BTADDR_SIZE); // for callback + bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms, + onCreateBondingResult, // callback + context_address, // user data + nat->adapter, + DBUS_CLASS_NAME, "CreateBonding", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + return ret ? JNI_TRUE : JNI_FALSE; + + } +#endif + return JNI_FALSE; +} + +static void cancelBondingProcessNative(JNIEnv *env, jobject object, + jstring address) { + LOGV(__FUNCTION__); + common_Bonding(env, object, -1, "CancelBondingProcess", address); +} + +static void removeBondingNative(JNIEnv *env, jobject object, jstring address) { + LOGV(__FUNCTION__); + common_Bonding(env, object, -1, "RemoveBonding", address); +} + +static jboolean hasBondingNative(JNIEnv *env, jobject object, jstring address) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + LOGV("... address = %s", c_address); + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "HasBonding", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jobjectArray listBondingsNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "ListBondings", + DBUS_TYPE_INVALID); + // return String[] + return reply ? dbus_returns_array_of_strings(env, reply) : NULL; + } +#endif + return NULL; +} + +static jobjectArray listConnectionsNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "ListConnections", + DBUS_TYPE_INVALID); + // return String[] + return reply ? dbus_returns_array_of_strings(env, reply) : NULL; + } +#endif + return NULL; +} + +static jobjectArray listRemoteDevicesNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "ListRemoteDevices", + DBUS_TYPE_INVALID); + return reply ? dbus_returns_array_of_strings(env, reply) : NULL; + } +#endif + return NULL; +} + +static jstring common_Get(JNIEnv *env, jobject object, const char *func) { + LOGV("%s:%s", __FUNCTION__, func); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusError err; + dbus_error_init(&err); + DBusMessage *reply = + dbus_func_args_error(env, nat->conn, &err, nat->adapter, + DBUS_CLASS_NAME, func, + DBUS_TYPE_INVALID); + if (reply) { + return dbus_returns_string(env, reply); + } else { + LOG_AND_FREE_DBUS_ERROR(&err); + return NULL; + } + } +#endif + return NULL; +} + +static jstring getAddressNative(JNIEnv *env, jobject obj) { + return common_Get(env, obj, "GetAddress"); +} + +static jstring getVersionNative(JNIEnv *env, jobject obj) { + return common_Get(env, obj, "GetVersion"); +} + +static jstring getRevisionNative(JNIEnv *env, jobject obj) { + return common_Get(env, obj, "GetRevision"); +} + +static jstring getManufacturerNative(JNIEnv *env, jobject obj) { + return common_Get(env, obj, "GetManufacturer"); +} + +static jstring getCompanyNative(JNIEnv *env, jobject obj) { + return common_Get(env, obj, "GetCompany"); +} + +static jboolean setNameNative(JNIEnv *env, jobject obj, jstring name) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, obj); + if (nat) { + const char *c_name = env->GetStringUTFChars(name, NULL); + DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "SetName", + DBUS_TYPE_STRING, &c_name, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(name, c_name); + if (reply) { + dbus_message_unref(reply); + return JNI_TRUE; + } + } +#endif + return JNI_FALSE; +} + +static jstring getMajorClassNative(JNIEnv *env, jobject obj) { + return common_Get(env, obj, "GetMajorClass"); +} + +static jstring getMinorClassNative(JNIEnv *env, jobject obj) { + return common_Get(env, obj, "GetMinorClass"); +} + +static jstring common_getRemote(JNIEnv *env, jobject object, const char *func, + jstring address) { + LOGV("%s:%s", __FUNCTION__, func); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + DBusError err; + dbus_error_init(&err); + + LOGV("... address = %s", c_address); + + DBusMessage *reply = + dbus_func_args_error(env, nat->conn, &err, nat->adapter, + DBUS_CLASS_NAME, func, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + if (reply) { + return dbus_returns_string(env, reply); + } else if (!strcmp(func, "GetRemoteName") && + dbus_error_has_name(&err, "org.bluez.Error.RequestDeferred")) { + // This error occurs if we request name during device discovery, + // its fine + LOGV("... %s: %s", func, err.message); + dbus_error_free(&err); + return NULL; + } else { + LOG_AND_FREE_DBUS_ERROR(&err); + return NULL; + } + } +#endif + return NULL; +} + +static jstring getRemoteAliasNative(JNIEnv *env, jobject obj, jstring address) { + return common_getRemote(env, obj, "GetRemoteAlias", address); +} + +static jboolean setRemoteAliasNative(JNIEnv *env, jobject obj, + jstring address, jstring alias) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, obj); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + const char *c_alias = env->GetStringUTFChars(alias, NULL); + + LOGV("... address = %s alias = %s", c_address, c_alias); + + DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "SetRemoteAlias", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_STRING, &c_alias, + DBUS_TYPE_INVALID); + + env->ReleaseStringUTFChars(address, c_address); + env->ReleaseStringUTFChars(alias, c_alias); + if (reply) + { + dbus_message_unref(reply); + return JNI_TRUE; + } + return JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jboolean clearRemoteAliasNative(JNIEnv *env, jobject obj, jstring address) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, obj); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + + LOGV("... address = %s", c_address); + + DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "ClearRemoteAlias", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + + env->ReleaseStringUTFChars(address, c_address); + if (reply) + { + dbus_message_unref(reply); + return JNI_TRUE; + } + return JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jstring getRemoteVersionNative(JNIEnv *env, jobject obj, jstring address) { + return common_getRemote(env, obj, "GetRemoteVersion", address); +} + +static jstring getRemoteRevisionNative(JNIEnv *env, jobject obj, jstring address) { + return common_getRemote(env, obj, "GetRemoteRevision", address); +} + +static jstring getRemoteManufacturerNative(JNIEnv *env, jobject obj, jstring address) { + return common_getRemote(env, obj, "GetRemoteManufacturer", address); +} + +static jstring getRemoteCompanyNative(JNIEnv *env, jobject obj, jstring address) { + return common_getRemote(env, obj, "GetRemoteCompany", address); +} + +static jstring getRemoteMajorClassNative(JNIEnv *env, jobject obj, jstring address) { + return common_getRemote(env, obj, "GetRemoteMajorClass", address); +} + +static jstring getRemoteMinorClassNative(JNIEnv *env, jobject obj, jstring address) { + return common_getRemote(env, obj, "GetRemoteMinorClass", address); +} + +static jstring getRemoteNameNative(JNIEnv *env, jobject obj, jstring address) { + return common_getRemote(env, obj, "GetRemoteName", address); +} + +static jstring lastSeenNative(JNIEnv *env, jobject obj, jstring address) { + return common_getRemote(env, obj, "LastSeen", address); +} + +static jstring lastUsedNative(JNIEnv *env, jobject obj, jstring address) { + return common_getRemote(env, obj, "LastUsed", address); +} + +static jobjectArray getRemoteServiceClassesNative(JNIEnv *env, jobject object, + jstring address) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + + LOGV("... address = %s", c_address); + + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "GetRemoteServiceClasses", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + return reply ? dbus_returns_array_of_strings(env, reply) : NULL; + } +#endif + return NULL; +} + +static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + jint ret = 0; + const char *c_address = env->GetStringUTFChars(address, NULL); + + LOGV("... address = %s", c_address); + + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "GetRemoteClass", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + if (reply) + { + DBusError err; + dbus_error_init(&err); + if (!dbus_message_get_args(reply, &err, + DBUS_TYPE_UINT32, &ret, + DBUS_TYPE_INVALID)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + } + dbus_message_unref(reply); + } + + return ret; + } +#endif + return 0; +} + +static jbyteArray getRemoteFeaturesNative(JNIEnv *env, jobject object, + jstring address) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + + LOGV("... address = %s", c_address); + + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "GetRemoteFeatures", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + /* array of DBUS_TYPE_BYTE_AS_STRING */ + return reply ? dbus_returns_array_of_bytes(env, reply) : NULL; + } +#endif + return NULL; +} + +static jintArray getRemoteServiceHandlesNative(JNIEnv *env, jobject object, + jstring address, jstring match) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + jintArray intArray = NULL; + const char *c_address = env->GetStringUTFChars(address, NULL); + const char *c_match = env->GetStringUTFChars(match, NULL); + + LOGV("... address = %s match = %s", c_address, c_match); + + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "GetRemoteServiceHandles", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_STRING, &c_match, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + env->ReleaseStringUTFChars(match, c_match); + if (reply) + { + DBusError err; + jint *list; + int i, len; + + dbus_error_init(&err); + if (dbus_message_get_args (reply, &err, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, + &list, &len, + DBUS_TYPE_INVALID)) { + if (len) { + intArray = env->NewIntArray(len); + if (intArray) + env->SetIntArrayRegion(intArray, 0, len, list); + } + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + } + + dbus_message_unref(reply); + } + return intArray; + } +#endif + return NULL; +} + +static jbyteArray getRemoteServiceRecordNative(JNIEnv *env, jobject object, + jstring address, jint handle) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + + LOGV("... address = %s", c_address); + + DBusMessage *reply = + dbus_func_args(env, nat->conn, nat->adapter, + DBUS_CLASS_NAME, "GetRemoteServiceRecord", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + return reply ? dbus_returns_array_of_bytes(env, reply) : NULL; + } +#endif + return NULL; +} + +static jboolean getRemoteServiceChannelNative(JNIEnv *env, jobject object, + jstring address, jshort uuid16) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); + strlcpy(context_address, c_address, BTADDR_SIZE); + + LOGV("... address = %s", c_address); + LOGV("... uuid16 = %#X", uuid16); + + bool ret = dbus_func_args_async(env, nat->conn, 20000, // ms + onGetRemoteServiceChannelResult, context_address, + nat->adapter, + DBUS_CLASS_NAME, "GetRemoteServiceChannel", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_UINT16, &uuid16, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jint enableNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + return bt_enable(); +#endif + return -1; +} + +static jint disableNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + return bt_disable(); +#endif + return -1; +} + +static jint isEnabledNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + return bt_is_enabled(); +#endif + return -1; +} + +static jboolean setPinNative(JNIEnv *env, jobject object, jstring address, + jstring pin, int nativeData) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg = (DBusMessage *)nativeData; + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply to return PIN code to " + "D-Bus\n", __FUNCTION__); + dbus_message_unref(msg); + return JNI_FALSE; + } + + const char *c_pin = env->GetStringUTFChars(pin, NULL); + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &c_pin, + DBUS_TYPE_INVALID); + + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(msg); + dbus_message_unref(reply); + env->ReleaseStringUTFChars(pin, c_pin); + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jboolean cancelPinNative(JNIEnv *env, jobject object, jstring address, + int nativeData) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg = (DBusMessage *)nativeData; + DBusMessage *reply = dbus_message_new_error(msg, + "org.bluez.Error.Canceled", "PIN Entry was canceled"); + if (!reply) { + LOGE("%s: Cannot create message reply to return PIN cancel to " + "D-BUS\n", __FUNCTION__); + dbus_message_unref(msg); + return JNI_FALSE; + } + + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(msg); + dbus_message_unref(reply); + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"classInitNative", "()V", (void*)classInitNative}, + {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative}, + {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, + {"getAdapterPathNative", "()Ljava/lang/String;", (void*)getAdapterPathNative}, + + {"isEnabledNative", "()I", (void *)isEnabledNative}, + {"enableNative", "()I", (void *)enableNative}, + {"disableNative", "()I", (void *)disableNative}, + + {"getAddressNative", "()Ljava/lang/String;", (void *)getAddressNative}, + {"getNameNative", "()Ljava/lang/String;", (void*)getNameNative}, + {"setNameNative", "(Ljava/lang/String;)Z", (void *)setNameNative}, + {"getMajorClassNative", "()Ljava/lang/String;", (void *)getMajorClassNative}, + {"getMinorClassNative", "()Ljava/lang/String;", (void *)getMinorClassNative}, + {"getVersionNative", "()Ljava/lang/String;", (void *)getVersionNative}, + {"getRevisionNative", "()Ljava/lang/String;", (void *)getRevisionNative}, + {"getManufacturerNative", "()Ljava/lang/String;", (void *)getManufacturerNative}, + {"getCompanyNative", "()Ljava/lang/String;", (void *)getCompanyNative}, + + {"getModeNative", "()Ljava/lang/String;", (void *)getModeNative}, + {"setModeNative", "(Ljava/lang/String;)Z", (void *)setModeNative}, + + {"getDiscoverableTimeoutNative", "()I", (void *)getDiscoverableTimeoutNative}, + {"setDiscoverableTimeoutNative", "(I)Z", (void *)setDiscoverableTimeoutNative}, + + {"startDiscoveryNative", "(Z)Z", (void*)startDiscoveryNative}, + {"cancelDiscoveryNative", "()Z", (void *)cancelDiscoveryNative}, + {"startPeriodicDiscoveryNative", "()Z", (void *)startPeriodicDiscoveryNative}, + {"stopPeriodicDiscoveryNative", "()Z", (void *)stopPeriodicDiscoveryNative}, + {"isPeriodicDiscoveryNative", "()Z", (void *)isPeriodicDiscoveryNative}, + {"listRemoteDevicesNative", "()[Ljava/lang/String;", (void *)listRemoteDevicesNative}, + + {"listConnectionsNative", "()[Ljava/lang/String;", (void *)listConnectionsNative}, + {"isConnectedNative", "(Ljava/lang/String;)Z", (void *)isConnectedNative}, + {"disconnectRemoteDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectRemoteDeviceNative}, + + {"createBondingNative", "(Ljava/lang/String;I)Z", (void *)createBondingNative}, + {"cancelBondingProcessNative", "(Ljava/lang/String;)Z", (void *)cancelBondingProcessNative}, + {"listBondingsNative", "()[Ljava/lang/String;", (void *)listBondingsNative}, + {"hasBondingNative", "(Ljava/lang/String;)Z", (void *)hasBondingNative}, + {"removeBondingNative", "(Ljava/lang/String;)Z", (void *)removeBondingNative}, + + {"getRemoteNameNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteNameNative}, + {"getRemoteAliasNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteAliasNative}, + {"setRemoteAliasNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)setRemoteAliasNative}, + {"clearRemoteAliasNative", "(Ljava/lang/String;)Z", (void *)clearRemoteAliasNative}, + {"getRemoteVersionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteVersionNative}, + {"getRemoteRevisionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteRevisionNative}, + {"getRemoteClassNative", "(Ljava/lang/String;)I", (void *)getRemoteClassNative}, + {"getRemoteManufacturerNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteManufacturerNative}, + {"getRemoteCompanyNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteCompanyNative}, + {"getRemoteMajorClassNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteMajorClassNative}, + {"getRemoteMinorClassNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteMinorClassNative}, + {"getRemoteServiceClassesNative", "(Ljava/lang/String;)[Ljava/lang/String;", (void *)getRemoteServiceClassesNative}, + {"getRemoteServiceChannelNative", "(Ljava/lang/String;S)Z", (void *)getRemoteServiceChannelNative}, + {"getRemoteFeaturesNative", "(Ljava/lang/String;)[B", (void *)getRemoteFeaturesNative}, + {"lastSeenNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastSeenNative}, + {"lastUsedNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastUsedNative}, + {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative}, + {"cancelPinNative", "(Ljava/lang/String;I)Z", (void *)cancelPinNative}, +}; + +int register_android_server_BluetoothDeviceService(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/server/BluetoothDeviceService", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp new file mode 100644 index 0000000..395a45c --- /dev/null +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -0,0 +1,642 @@ +/* +** 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 "BluetoothEventLoop.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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> + +#ifdef HAVE_BLUETOOTH +#include <dbus/dbus.h> +#endif + +namespace android { + +#ifdef HAVE_BLUETOOTH +static jfieldID field_mNativeData; + +static jmethodID method_onModeChanged; +static jmethodID method_onDiscoveryStarted; +static jmethodID method_onDiscoveryCompleted; +static jmethodID method_onRemoteDeviceFound; +static jmethodID method_onRemoteDeviceDisappeared; +static jmethodID method_onRemoteClassUpdated; +static jmethodID method_onRemoteNameUpdated; +static jmethodID method_onRemoteNameFailed; +static jmethodID method_onRemoteAliasChanged; +static jmethodID method_onRemoteAliasCleared; +static jmethodID method_onRemoteDeviceConnected; +static jmethodID method_onRemoteDeviceDisconnectRequested; +static jmethodID method_onRemoteDeviceDisconnected; +static jmethodID method_onBondingCreated; +static jmethodID method_onBondingRemoved; + +static jmethodID method_onCreateBondingResult; +static jmethodID method_onGetRemoteServiceChannelResult; + +static jmethodID method_onPasskeyAgentRequest; +static jmethodID method_onPasskeyAgentCancel; + +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; +}; + +// Only valid during waitForAndDispatchEventNative() +static 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, + field_mNativeData)); +} + +#endif +static void classInitNative(JNIEnv* env, jclass clazz) { + LOGV(__FUNCTION__); + +#ifdef HAVE_BLUETOOTH + method_onModeChanged = env->GetMethodID(clazz, "onModeChanged", "(Ljava/lang/String;)V"); + method_onDiscoveryStarted = env->GetMethodID(clazz, "onDiscoveryStarted", "()V"); + method_onDiscoveryCompleted = env->GetMethodID(clazz, "onDiscoveryCompleted", "()V"); + method_onRemoteDeviceFound = env->GetMethodID(clazz, "onRemoteDeviceFound", "(Ljava/lang/String;IS)V"); + method_onRemoteDeviceDisappeared = env->GetMethodID(clazz, "onRemoteDeviceDisappeared", "(Ljava/lang/String;)V"); + method_onRemoteClassUpdated = env->GetMethodID(clazz, "onRemoteClassUpdated", "(Ljava/lang/String;I)V"); + method_onRemoteNameUpdated = env->GetMethodID(clazz, "onRemoteNameUpdated", "(Ljava/lang/String;Ljava/lang/String;)V"); + method_onRemoteNameFailed = env->GetMethodID(clazz, "onRemoteNameFailed", "(Ljava/lang/String;)V"); + method_onRemoteAliasChanged = env->GetMethodID(clazz, "onRemoteAliasChanged", "(Ljava/lang/String;Ljava/lang/String;)V"); + method_onRemoteDeviceConnected = env->GetMethodID(clazz, "onRemoteDeviceConnected", "(Ljava/lang/String;)V"); + method_onRemoteDeviceDisconnectRequested = env->GetMethodID(clazz, "onRemoteDeviceDisconnectRequested", "(Ljava/lang/String;)V"); + method_onRemoteDeviceDisconnected = env->GetMethodID(clazz, "onRemoteDeviceDisconnected", "(Ljava/lang/String;)V"); + method_onBondingCreated = env->GetMethodID(clazz, "onBondingCreated", "(Ljava/lang/String;)V"); + method_onBondingRemoved = env->GetMethodID(clazz, "onBondingRemoved", "(Ljava/lang/String;)V"); + + method_onCreateBondingResult = env->GetMethodID(clazz, "onCreateBondingResult", "(Ljava/lang/String;Z)V"); + + method_onPasskeyAgentRequest = env->GetMethodID(clazz, "onPasskeyAgentRequest", "(Ljava/lang/String;I)V"); + method_onPasskeyAgentCancel = env->GetMethodID(clazz, "onPasskeyAgentCancel", "(Ljava/lang/String;)V"); + method_onGetRemoteServiceChannelResult = env->GetMethodID(clazz, "onGetRemoteServiceChannelResult", "(Ljava/lang/String;I)V"); + + field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); +#endif +} + +static void initializeNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t)); + if (NULL == nat) { + LOGE("%s: out of memory!", __FUNCTION__); + return; + } + env->SetIntField(object, field_mNativeData, (jint)nat); + + { + DBusError err; + dbus_error_init(&err); + dbus_threads_init_default(); + nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (dbus_error_is_set(&err)) { + LOGE("%s: Could not get onto the system bus!", __FUNCTION__); + dbus_error_free(&err); + } + } +#endif +} + +static void cleanupNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = + (native_data_t *)env->GetIntField(object, field_mNativeData); + if (nat) { + free(nat); + } +#endif +} + +#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, + DBusMessage *msg, + void *data); + +static const DBusObjectPathVTable passkey_agent_vtable = { + NULL, passkey_agent_event_filter, NULL, NULL, NULL, NULL +}; +#endif + +static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + dbus_threads_init_default(); + native_data_t *nat = get_native_data(env, object); + if (nat != NULL && nat->conn != NULL) { + if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){ + return JNI_FALSE; + } + + if (add_adapter_event_match(env, nat) != JNI_TRUE) { + return JNI_FALSE; + } + + const char *path = "/android/bluetooth/PasskeyAgent"; + if (!dbus_connection_register_object_path(nat->conn, path, + &passkey_agent_vtable, NULL)) { + LOGE("%s: Can't register object path %s for agent!", + __FUNCTION__, path); + return JNI_FALSE; + } + + DBusError err; + dbus_error_init(&err); + + // RegisterDefaultPasskeyAgent() will fail until hcid is up, so keep + // trying for 10 seconds. + int attempt; + for (attempt = 1000; attempt > 0; attempt--) { + DBusMessage *reply = dbus_func_args_error(env, nat->conn, &err, + BLUEZ_DBUS_BASE_PATH, + "org.bluez.Security", "RegisterDefaultPasskeyAgent", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + if (reply) { + // Success + dbus_message_unref(reply); + return JNI_TRUE; + } else if (dbus_error_has_name(&err, + "org.freedesktop.DBus.Error.ServiceUnknown")) { + // hcid is still down, retry + dbus_error_free(&err); + usleep(10000); // 10 ms + } else { + // Some other error we weren't expecting + LOG_AND_FREE_DBUS_ERROR(&err); + return JNI_FALSE; + } + } + LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), " + "is hcid running?"); + return JNI_FALSE; + } + +#endif + return JNI_FALSE; +} + +static void tearDownEventLoopNative(JNIEnv *env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat != NULL && nat->conn != NULL) { + + const char *path = "/android/bluetooth/PasskeyAgent"; + DBusMessage *reply = + dbus_func_args(env, nat->conn, BLUEZ_DBUS_BASE_PATH, + "org.bluez.Security", "UnregisterDefaultPasskeyAgent", + DBUS_TYPE_STRING, &path, + 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); + } +#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); + } +} + +// Called by dbus during WaitForAndDispatchEventNative() +static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, + void *data) { + native_data_t *nat; + JNIEnv *env; + DBusError err; + + dbus_error_init(&err); + + nat = (native_data_t *)data; + env = nat->env; + if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) { + LOGV("%s: not interested (not a signal).", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + LOGV("%s: Received signal %s:%s", __FUNCTION__, + dbus_message_get_interface(msg), dbus_message_get_member(msg)); + + if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "RemoteDeviceFound")) { + char *c_address; + int n_class; + short n_rssi; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_UINT32, &n_class, + DBUS_TYPE_INT16, &n_rssi, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s class = %#X rssi = %hd", c_address, n_class, + n_rssi); + env->CallVoidMethod(nat->me, + method_onRemoteDeviceFound, + env->NewStringUTF(c_address), + (jint)n_class, + (jshort)n_rssi); + } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "DiscoveryStarted")) { + LOGI("DiscoveryStarted signal received"); + env->CallVoidMethod(nat->me, method_onDiscoveryStarted); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "DiscoveryCompleted")) { + LOGI("DiscoveryCompleted signal received"); + env->CallVoidMethod(nat->me, method_onDiscoveryCompleted); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "RemoteDeviceDisappeared")) { + char *c_address; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s", c_address); + env->CallVoidMethod(nat->me, method_onRemoteDeviceDisappeared, + env->NewStringUTF(c_address)); + } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "RemoteClassUpdated")) { + char *c_address; + int n_class; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_UINT32, &n_class, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s", c_address); + env->CallVoidMethod(nat->me, method_onRemoteClassUpdated, + env->NewStringUTF(c_address), (jint)n_class); + } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "RemoteNameUpdated")) { + char *c_address; + char *c_name; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_STRING, &c_name, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s, name = %s", c_address, c_name); + env->CallVoidMethod(nat->me, + method_onRemoteNameUpdated, + env->NewStringUTF(c_address), + env->NewStringUTF(c_name)); + } 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", + "RemoteNameFailed")) { + char *c_address; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s", c_address); + env->CallVoidMethod(nat->me, + method_onRemoteNameFailed, + env->NewStringUTF(c_address)); + } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "RemoteAliasChanged")) { + char *c_address, *c_alias; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_STRING, &c_alias, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s, alias = %s", c_address, c_alias); + env->CallVoidMethod(nat->me, + method_onRemoteAliasChanged, + env->NewStringUTF(c_address), + env->NewStringUTF(c_alias)); + } 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", + "RemoteAliasCleared")) { + char *c_address; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s", c_address); + env->CallVoidMethod(nat->me, + method_onRemoteAliasCleared, + env->NewStringUTF(c_address)); + } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "RemoteDeviceConnected")) { + char *c_address; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s", c_address); + env->CallVoidMethod(nat->me, + method_onRemoteDeviceConnected, + env->NewStringUTF(c_address)); + } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "RemoteDeviceDisconnectRequested")) { + char *c_address; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s", c_address); + env->CallVoidMethod(nat->me, + method_onRemoteDeviceDisconnectRequested, + env->NewStringUTF(c_address)); + } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "RemoteDeviceDisconnected")) { + char *c_address; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s", c_address); + env->CallVoidMethod(nat->me, + method_onRemoteDeviceDisconnected, + env->NewStringUTF(c_address)); + } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "BondingCreated")) { + char *c_address; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s", c_address); + env->CallVoidMethod(nat->me, + method_onBondingCreated, + env->NewStringUTF(c_address)); + } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(msg, + "org.bluez.Adapter", + "BondingRemoved")) { + char *c_address; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID)) { + LOGV("... address = %s", c_address); + env->CallVoidMethod(nat->me, + method_onBondingRemoved, + env->NewStringUTF(c_address)); + } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; + } else { + LOGV("... ignored"); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +// Called by dbus during WaitForAndDispatchEventNative() +static DBusHandlerResult passkey_agent_event_filter(DBusConnection *conn, + DBusMessage *msg, + void *data) { + native_data_t *nat = event_loop_nat; + JNIEnv *env; + + + env = nat->env; + if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) { + LOGV("%s: not interested (not a method call).", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + LOGV("%s: Received method %s:%s", __FUNCTION__, + dbus_message_get_interface(msg), dbus_message_get_member(msg)); + + if (dbus_message_is_method_call(msg, + "org.bluez.PasskeyAgent", "Request")) { + + const char *adapter; + const char *address; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &adapter, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for Request() method", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + LOGV("... address = %s", address); + + dbus_message_ref(msg); // increment refcount because we pass to java + + env->CallVoidMethod(nat->me, method_onPasskeyAgentRequest, + env->NewStringUTF(address), (int)msg); + + return DBUS_HANDLER_RESULT_HANDLED; + + } else if (dbus_message_is_method_call(msg, + "org.bluez.PasskeyAgent", "Cancel")) { + + const char *adapter; + const char *address; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &adapter, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + LOGV("... address = %s", address); + + env->CallVoidMethod(nat->me, method_onPasskeyAgentCancel, + env->NewStringUTF(address)); + + return DBUS_HANDLER_RESULT_HANDLED; + + } else if (dbus_message_is_method_call(msg, + "org.bluez.PasskeyAgent", "Release")) { + LOGE("We are no longer the passkey agent!"); + } else { + LOGV("... ignored"); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} +#endif + +static jboolean waitForAndDispatchEventNative(JNIEnv *env, jobject object, + jint timeout_ms) { +#ifdef HAVE_BLUETOOTH + //LOGV("%s: %8d (pid %d tid %d)",__FUNCTION__, time(NULL), getpid(), gettid()); // too chatty + native_data_t *nat = get_native_data(env, object); + if (nat != NULL && nat->conn != NULL) { + jboolean ret; + 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; + event_loop_nat = NULL; + nat->me = NULL; + nat->env = NULL; + return ret; + } +#endif + return JNI_FALSE; +} + +#ifdef HAVE_BLUETOOTH +void onCreateBondingResult(DBusMessage *msg, void *user) { + LOGV(__FUNCTION__); + + const char *address = (const char *)user; + DBusError err; + dbus_error_init(&err); + JNIEnv *env = event_loop_nat->env; + + LOGV("... address = %s", address); + + jboolean result = JNI_TRUE; + 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); + result = JNI_FALSE; + dbus_error_free(&err); + } + + env->CallVoidMethod(event_loop_nat->me, + method_onCreateBondingResult, + env->NewStringUTF(address), + result); + free(user); +} + +void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user) { + LOGV(__FUNCTION__); + + const char *address = (const char *) user; + DBusError err; + dbus_error_init(&err); + JNIEnv *env = event_loop_nat->env; + jint channel = -2; + + LOGV("... address = %s", context->address); + + if (dbus_set_error_from_message(&err, msg) || + !dbus_message_get_args(msg, &err, + DBUS_TYPE_INT32, &channel, + DBUS_TYPE_INVALID)) { + /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */ + LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); + dbus_error_free(&err); + } + +done: + env->CallVoidMethod(event_loop_nat->me, + method_onGetRemoteServiceChannelResult, + env->NewStringUTF(address), + channel); + free(user); +} +#endif + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"classInitNative", "()V", (void *)classInitNative}, + {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative}, + {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, + {"setUpEventLoopNative", "()Z", (void *)setUpEventLoopNative}, + {"tearDownEventLoopNative", "()V", (void *)tearDownEventLoopNative}, + {"waitForAndDispatchEventNative", "(I)Z", (void *)waitForAndDispatchEventNative} +}; + +int register_android_server_BluetoothEventLoop(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/server/BluetoothEventLoop", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp new file mode 100644 index 0000000..97daed3 --- /dev/null +++ b/core/jni/android_text_AndroidCharacter.cpp @@ -0,0 +1,128 @@ +/* //device/libs/android_runtime/android_text_AndroidCharacter.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "AndroidUnicode" + +#include <jni.h> +#include <android_runtime/AndroidRuntime.h> +#include "utils/misc.h" +#include "utils/AndroidUnicode.h" +#include "utils/Log.h" + +namespace android { + +static void jniThrowException(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + jclass excClazz = env->FindClass(exc); + LOG_ASSERT(excClazz, "Unable to find class %s", exc); + + env->ThrowNew(excClazz, msg); +} + +static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray, jbyteArray destArray, int count) +{ + jchar* src = env->GetCharArrayElements(srcArray, NULL); + jbyte* dest = env->GetByteArrayElements(destArray, NULL); + if (src == NULL || dest == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + goto DIRECTION_END; + } + + if (env->GetArrayLength(srcArray) < count || env->GetArrayLength(destArray) < count) { + jniThrowException(env, "java/lang/ArrayIndexException", NULL); + goto DIRECTION_END; + } + + for (int i = 0; i < count; i++) { + if (src[i] >= 0xD800 && src[i] <= 0xDBFF && + i + 1 < count && + src[i + 1] >= 0xDC00 && src[i + 1] <= 0xDFFF) { + int c = 0x00010000 + ((src[i] - 0xD800) << 10) + + (src[i + 1] & 0x3FF); + int dir = android::Unicode::getDirectionality(c); + + dest[i++] = dir; + dest[i] = dir; + } else { + int c = src[i]; + int dir = android::Unicode::getDirectionality(c); + + dest[i] = dir; + } + } + +DIRECTION_END: + env->ReleaseCharArrayElements(srcArray, src, JNI_ABORT); + env->ReleaseByteArrayElements(destArray, dest, JNI_ABORT); +} + +static jboolean mirror(JNIEnv* env, jobject obj, jcharArray charArray, int start, int count) +{ + jchar* data = env->GetCharArrayElements(charArray, NULL); + bool ret = false; + + if (data == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + goto MIRROR_END; + } + + if (start > start + count || env->GetArrayLength(charArray) < count) { + jniThrowException(env, "java/lang/ArrayIndexException", NULL); + goto MIRROR_END; + } + + for (int i = start; i < start + count; i++) { + // XXX this thinks it knows that surrogates are never mirrored + + int c1 = data[i]; + int c2 = android::Unicode::toMirror(c1); + + if (c1 != c2) { + data[i] = c2; + ret = true; + } + } + +MIRROR_END: + env->ReleaseCharArrayElements(charArray, data, JNI_ABORT); + return ret; +} + +static jchar getMirror(JNIEnv* env, jobject obj, jchar c) +{ + return android::Unicode::toMirror(c); +} + +static JNINativeMethod gMethods[] = { + { "getDirectionalities", "([C[BI)V", + (void*) getDirectionalities }, + { "mirror", "([CII)Z", + (void*) mirror }, + { "getMirror", "(C)C", + (void*) getMirror } +}; + +int register_android_text_AndroidCharacter(JNIEnv* env) +{ + jclass clazz = env->FindClass("android/text/AndroidCharacter"); + LOG_ASSERT(clazz, "Cannot find android/text/AndroidCharacter"); + + return AndroidRuntime::registerNativeMethods(env, "android/text/AndroidCharacter", + gMethods, NELEM(gMethods)); +} + +} diff --git a/core/jni/android_text_KeyCharacterMap.cpp b/core/jni/android_text_KeyCharacterMap.cpp new file mode 100644 index 0000000..2a23a71 --- /dev/null +++ b/core/jni/android_text_KeyCharacterMap.cpp @@ -0,0 +1,173 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include <ui/KeyCharacterMap.h> + +#include <nativehelper/jni.h> +#include <android_runtime/AndroidRuntime.h> +#include <nativehelper/JNIHelp.h> + +namespace android { + +static jint +ctor(JNIEnv *env, jobject clazz, jint id) +{ + return reinterpret_cast<int>(KeyCharacterMap::load(id)); +} + +static void +dtor(JNIEnv *env, jobject clazz, jint ptr) +{ + delete reinterpret_cast<KeyCharacterMap*>(ptr); +} + +static jchar +get(JNIEnv *env, jobject clazz, jint ptr, jint keycode, jint meta) +{ + return reinterpret_cast<KeyCharacterMap*>(ptr)->get(keycode, meta); +} + +static jchar +getNumber(JNIEnv *env, jobject clazz, jint ptr, jint keycode) +{ + return reinterpret_cast<KeyCharacterMap*>(ptr)->getNumber(keycode); +} + +static jchar +getMatch(JNIEnv *env, jobject clazz, jint ptr, jint keycode, jcharArray chars, jint modifiers) +{ + jchar rv; + jchar* ch = env->GetCharArrayElements(chars, NULL); + jsize chsize = env->GetArrayLength(chars); + + rv = reinterpret_cast<KeyCharacterMap*>(ptr)->getMatch(keycode, ch, chsize, modifiers); + + env->ReleaseCharArrayElements(chars, ch, JNI_ABORT); + return rv; +} + +static jchar +getDisplayLabel(JNIEnv *env, jobject clazz, jint ptr, jint keycode) +{ + return reinterpret_cast<KeyCharacterMap*>(ptr)->getDisplayLabel(keycode); +} + +static jfieldID gKeyDataMetaField; +static jfieldID gKeyDataNumberField; +static jfieldID gKeyDataDisplayLabelField; + +static jboolean +getKeyData(JNIEnv *env, jobject clazz, jint ptr, jint keycode, jobject keydata) +{ + jboolean rv; + + unsigned short displayLabel = env->GetCharField(keydata, gKeyDataDisplayLabelField); + unsigned short number = env->GetCharField(keydata, gKeyDataNumberField); + + jcharArray chars = (jcharArray) env->GetObjectField(keydata, gKeyDataMetaField); + jchar* ch = env->GetCharArrayElements(chars, NULL); + + KeyCharacterMap* kmap = reinterpret_cast<KeyCharacterMap*>(ptr); + rv = kmap->getKeyData(keycode, &displayLabel, &number, ch); + + env->SetCharField(keydata, gKeyDataDisplayLabelField, displayLabel); + env->SetCharField(keydata, gKeyDataNumberField, number); + + env->ReleaseCharArrayElements(chars, ch, 0); + return rv; +} + +static jint +getKeyboardType(JNIEnv *env, jobject clazz, jint ptr) +{ + return reinterpret_cast<KeyCharacterMap*>(ptr)->getKeyboardType(); +} + +static jlongArray +getEvents(JNIEnv *env, jobject clazz, jint ptr, jcharArray jchars) +{ + KeyCharacterMap* kmap = reinterpret_cast<KeyCharacterMap*>(ptr); + + uint16_t* chars = env->GetCharArrayElements(jchars, NULL); + size_t len = env->GetArrayLength(jchars); + + Vector<int32_t> keys; + Vector<uint32_t> modifiers; + bool success = kmap->getEvents(chars, len, &keys, &modifiers); + + env->ReleaseCharArrayElements(jchars, chars, JNI_ABORT); + + if (success) { + size_t N = keys.size(); + + jlongArray rv = env->NewLongArray(N); + uint64_t* results = (uint64_t*)env->GetLongArrayElements(rv, NULL); + + for (size_t i=0; i<N; i++) { + uint64_t v = modifiers[i]; + v <<= 32; + v |= keys[i]; + results[i] = v; + } + + env->ReleaseLongArrayElements(rv, (jlong*)results, 0); + return rv; + } else { + return NULL; + } +} + +// ============================================================================ +/* + * JNI registration. + */ + +static JNINativeMethod g_methods[] = { + /* name, signature, funcPtr */ + { "ctor_native", "(I)I", (void*)ctor }, + { "dtor_native", "(I)V", (void*)dtor }, + { "get_native", "(III)C", (void*)get }, + { "getNumber_native", "(II)C", (void*)getNumber }, + { "getMatch_native", "(II[CI)C", (void*)getMatch }, + { "getDisplayLabel_native", "(II)C", (void*)getDisplayLabel }, + { "getKeyData_native", "(IILandroid/view/KeyCharacterMap$KeyData;)Z", + (void*)getKeyData }, + { "getKeyboardType_native", "(I)I", (void*)getKeyboardType }, + { "getEvents_native", "(I[C)[J", (void*)getEvents } +}; + +int register_android_text_KeyCharacterMap(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass("android/view/KeyCharacterMap$KeyData"); + if (clazz == NULL) { + LOGE("Can't find android/view/KeyCharacterMap$KeyData"); + return -1; + } + + gKeyDataMetaField = env->GetFieldID(clazz, "meta", "[C"); + gKeyDataNumberField = env->GetFieldID(clazz, "number", "C"); + gKeyDataDisplayLabelField = env->GetFieldID(clazz, "displayLabel", "C"); + + return AndroidRuntime::registerNativeMethods(env, + "android/view/KeyCharacterMap", g_methods, NELEM(g_methods)); +} + +}; // namespace android + + + diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp new file mode 100644 index 0000000..8a62159 --- /dev/null +++ b/core/jni/android_util_AssetManager.cpp @@ -0,0 +1,1673 @@ +/* //device/libs/android_runtime/android_util_AssetManager.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "asset" + +#include <android_runtime/android_util_AssetManager.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_util_Binder.h" +#include <utils/misc.h> +#include <android_runtime/AndroidRuntime.h> +#include <utils/Log.h> + +#include <utils/Asset.h> +#include <utils/AssetManager.h> +#include <utils/ResourceTypes.h> + +#include <stdio.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +static struct typedvalue_offsets_t +{ + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; +} gTypedValueOffsets; + +static struct assetfiledescriptor_offsets_t +{ + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; +} gAssetFileDescriptorOffsets; + +static struct assetmanager_offsets_t +{ + jfieldID mObject; +} gAssetManagerOffsets; + +jclass g_stringClass = NULL; + +// ---------------------------------------------------------------------------- + +static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + jclass npeClazz; + + npeClazz = env->FindClass(exc); + LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc); + + env->ThrowNew(npeClazz, msg); +} + +enum { + STYLE_NUM_ENTRIES = 5, + STYLE_TYPE = 0, + STYLE_DATA = 1, + STYLE_ASSET_COOKIE = 2, + STYLE_RESOURCE_ID = 3, + STYLE_CHANGING_CONFIGURATIONS = 4 +}; + +static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags) +{ + env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, + (jint)table->getTableCookie(block)); + env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); + env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); + env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, + typeSpecFlags); + return block; +} + +// ---------------------------------------------------------------------------- + +// this guy is exported to other jni routines +AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) +{ + AssetManager* am = (AssetManager*)env->GetIntField(obj, gAssetManagerOffsets.mObject); + if (am != NULL) { + return am; + } + jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); + return NULL; +} + +static jint android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, + jstring fileName, jint mode) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + LOGV("openAsset in %p (Java object %p)\n", am, clazz); + + if (fileName == NULL || am == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return -1; + } + + if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM + && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { + doThrow(env, "java/lang/IllegalArgumentException"); + return -1; + } + + const char* fileName8 = env->GetStringUTFChars(fileName, NULL); + Asset* a = am->open(fileName8, (Asset::AccessMode)mode); + + if (a == NULL) { + doThrow(env, "java/io/FileNotFoundException", fileName8); + env->ReleaseStringUTFChars(fileName, fileName8); + return -1; + } + env->ReleaseStringUTFChars(fileName, fileName8); + + //printf("Created Asset Stream: %p\n", a); + + return (jint)a; +} + +static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) +{ + off_t startOffset, length; + int fd = a->openFileDescriptor(&startOffset, &length); + delete a; + + if (fd < 0) { + doThrow(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably compressed"); + return NULL; + } + + jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); + if (offsets == NULL) { + close(fd); + return NULL; + } + + offsets[0] = startOffset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); + + jobject fileDesc = newFileDescriptor(env, fd); + if (fileDesc == NULL) { + close(fd); + return NULL; + } + + return newParcelFileDescriptor(env, fileDesc); +} + +static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, + jstring fileName, jlongArray outOffsets) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + LOGV("openAssetFd in %p (Java object %p)\n", am, clazz); + + if (fileName == NULL || am == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return NULL; + } + + const char* fileName8 = env->GetStringUTFChars(fileName, NULL); + Asset* a = am->open(fileName8, Asset::ACCESS_RANDOM); + + if (a == NULL) { + doThrow(env, "java/io/FileNotFoundException", fileName8); + env->ReleaseStringUTFChars(fileName, fileName8); + return NULL; + } + env->ReleaseStringUTFChars(fileName, fileName8); + + //printf("Created Asset Stream: %p\n", a); + + return returnParcelFileDescriptor(env, a, outOffsets); +} + +static jint android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName, + jint mode) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + LOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); + + if (fileName == NULL || am == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return -1; + } + + if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM + && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { + doThrow(env, "java/lang/IllegalArgumentException"); + return -1; + } + + const char* fileName8 = env->GetStringUTFChars(fileName, NULL); + Asset* a = cookie + ? am->openNonAsset((void*)cookie, fileName8, (Asset::AccessMode)mode) + : am->openNonAsset(fileName8, (Asset::AccessMode)mode); + + if (a == NULL) { + doThrow(env, "java/io/FileNotFoundException", fileName8); + env->ReleaseStringUTFChars(fileName, fileName8); + return -1; + } + env->ReleaseStringUTFChars(fileName, fileName8); + + //printf("Created Asset Stream: %p\n", a); + + return (jint)a; +} + +static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName, + jlongArray outOffsets) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + LOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); + + if (fileName == NULL || am == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return NULL; + } + + const char* fileName8 = env->GetStringUTFChars(fileName, NULL); + Asset* a = cookie + ? am->openNonAsset((void*)cookie, fileName8, Asset::ACCESS_RANDOM) + : am->openNonAsset(fileName8, Asset::ACCESS_RANDOM); + + if (a == NULL) { + doThrow(env, "java/io/FileNotFoundException", fileName8); + env->ReleaseStringUTFChars(fileName, fileName8); + return NULL; + } + env->ReleaseStringUTFChars(fileName, fileName8); + + //printf("Created Asset Stream: %p\n", a); + + return returnParcelFileDescriptor(env, a, outOffsets); +} + +static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, + jstring fileName) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + if (fileName == NULL || am == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return NULL; + } + + const char* fileName8 = env->GetStringUTFChars(fileName, NULL); + + AssetDir* dir = am->openDir(fileName8); + + env->ReleaseStringUTFChars(fileName, fileName8); + + if (dir == NULL) { + doThrow(env, "java/io/FileNotFoundException", fileName8); + return NULL; + } + + jclass cls = env->FindClass("java/lang/String"); + LOG_FATAL_IF(cls == NULL, "No string class?!?"); + if (cls == NULL) { + delete dir; + return NULL; + } + + size_t N = dir->getFileCount(); + + jobjectArray array = env->NewObjectArray(dir->getFileCount(), + cls, NULL); + if (array == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + delete dir; + return NULL; + } + + for (size_t i=0; i<N; i++) { + const String8& name = dir->getFileName(i); + jstring str = env->NewStringUTF(name.string()); + if (str == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + delete dir; + return NULL; + } + env->SetObjectArrayElement(array, i, str); + } + + delete dir; + + return array; +} + +static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, + jint asset) +{ + Asset* a = (Asset*)asset; + + //printf("Destroying Asset Stream: %p\n", a); + + if (a == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return; + } + + delete a; +} + +static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, + jint asset) +{ + Asset* a = (Asset*)asset; + + if (a == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return -1; + } + + uint8_t b; + ssize_t res = a->read(&b, 1); + return res == 1 ? b : -1; +} + +static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, + jint asset, jbyteArray bArray, + jint off, jint len) +{ + Asset* a = (Asset*)asset; + + if (a == NULL || bArray == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return -1; + } + + if (len == 0) { + return 0; + } + + jsize bLen = env->GetArrayLength(bArray); + if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { + doThrow(env, "java/lang/IndexOutOfBoundsException"); + return -1; + } + + jbyte* b = env->GetByteArrayElements(bArray, NULL); + ssize_t res = a->read(b+off, len); + env->ReleaseByteArrayElements(bArray, b, 0); + + if (res > 0) return res; + + if (res < 0) { + doThrow(env, "java/io/IOException"); + } + return -1; +} + +static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, + jint asset, + jlong offset, jint whence) +{ + Asset* a = (Asset*)asset; + + if (a == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return -1; + } + + return a->seek( + offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); +} + +static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, + jint asset) +{ + Asset* a = (Asset*)asset; + + if (a == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return -1; + } + + return a->getLength(); +} + +static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, + jint asset) +{ + Asset* a = (Asset*)asset; + + if (a == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return -1; + } + + return a->getRemainingLength(); +} + +static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, + jstring path) +{ + if (path == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + + const char* path8 = env->GetStringUTFChars(path, NULL); + + void* cookie; + bool res = am->addAssetPath(String8(path8), &cookie); + + env->ReleaseStringUTFChars(path, path8); + + return (res) ? (jint)cookie : 0; +} + +static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_TRUE; + } + return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; +} + +static void android_content_AssetManager_setLocale(JNIEnv* env, jobject clazz, + jstring locale) +{ + if (locale == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return; + } + + const char* locale8 = env->GetStringUTFChars(locale, NULL); + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return; + } + + am->setLocale(locale8); + + env->ReleaseStringUTFChars(locale, locale8); +} + +static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) +{ + Vector<String8> locales; + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + am->getLocales(&locales); + + const int N = locales.size(); + + jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); + if (result == NULL) { + return NULL; + } + + for (int i=0; i<N; i++) { + LOGD("locale %2d: '%s'", i, locales[i].string()); + env->SetObjectArrayElement(result, i, env->NewStringUTF(locales[i].string())); + } + + return result; +} + +static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, + jint mcc, jint mnc, + jstring locale, jint orientation, + jint touchscreen, jint density, + jint keyboard, jint keyboardHidden, + jint navigation, + jint screenWidth, jint screenHeight, + jint sdkVersion) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return; + } + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; + + config.mcc = (uint16_t)mcc; + config.mnc = (uint16_t)mnc; + config.orientation = (uint8_t)orientation; + config.touchscreen = (uint8_t)touchscreen; + config.density = (uint16_t)density; + config.keyboard = (uint8_t)keyboard; + config.inputFlags = (uint8_t)keyboardHidden<<ResTable_config::SHIFT_KEYSHIDDEN; + config.navigation = (uint8_t)navigation; + config.screenWidth = (uint16_t)screenWidth; + config.screenHeight = (uint16_t)screenHeight; + config.sdkVersion = (uint16_t)sdkVersion; + config.minorVersion = 0; + am->setConfiguration(config, locale8); + + if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); +} + +static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, + jstring name, + jstring defType, + jstring defPackage) +{ + if (name == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + const char16_t* name16 = env->GetStringChars(name, NULL); + jsize nameLen = env->GetStringLength(name); + const char16_t* defType16 = defType + ? env->GetStringChars(defType, NULL) : NULL; + jsize defTypeLen = defType + ? env->GetStringLength(defType) : 0; + const char16_t* defPackage16 = defPackage + ? env->GetStringChars(defPackage, NULL) : NULL; + jsize defPackageLen = defPackage + ? env->GetStringLength(defPackage) : 0; + + jint ident = am->getResources().identifierForName( + name16, nameLen, defType16, defTypeLen, defPackage16, defPackageLen); + + if (defPackage16) { + env->ReleaseStringChars(defPackage, defPackage16); + } + if (defType16) { + env->ReleaseStringChars(defType, defType16); + } + env->ReleaseStringChars(name, name16); + + return ident; +} + +static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, &name)) { + return NULL; + } + + String16 str; + if (name.package != NULL) { + str.setTo(name.package, name.packageLen); + } + if (name.type != NULL) { + if (str.size() > 0) { + char16_t div = ':'; + str.append(&div, 1); + } + str.append(name.type, name.typeLen); + } + if (name.name != NULL) { + if (str.size() > 0) { + char16_t div = '/'; + str.append(&div, 1); + } + str.append(name.name, name.nameLen); + } + + return env->NewString((const jchar*)str.string(), str.size()); +} + +static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, &name)) { + return NULL; + } + + if (name.package != NULL) { + return env->NewString((const jchar*)name.package, name.packageLen); + } + + return NULL; +} + +static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, &name)) { + return NULL; + } + + if (name.type != NULL) { + return env->NewString((const jchar*)name.type, name.typeLen); + } + + return NULL; +} + +static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, &name)) { + return NULL; + } + + if (name.name != NULL) { + return env->NewString((const jchar*)name.name, name.nameLen); + } + + return NULL; +} + +static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, + jint ident, + jobject outValue, + jboolean resolve) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + const ResTable& res(am->getResources()); + + Res_value value; + uint32_t typeSpecFlags; + ssize_t block = res.getResource(ident, &value, false, &typeSpecFlags); + uint32_t ref = ident; + if (resolve) { + block = res.resolveReference(&value, block, &ref); + } + return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; +} + +static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, + jint ident, jint bagEntryId, + jobject outValue, jboolean resolve) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + const ResTable& res(am->getResources()); + + // Now lock down the resource object and start pulling stuff from it. + res.lock(); + + ssize_t block = -1; + Res_value value; + + const ResTable::bag_entry* entry = NULL; + uint32_t typeSpecFlags; + ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); + + for (ssize_t i=0; i<entryCount; i++) { + if (((uint32_t)bagEntryId) == entry->map.name.ident) { + block = entry->stringBlock; + value = entry->map.value; + } + entry++; + } + + res.unlock(); + + if (block < 0) { + return block; + } + + uint32_t ref = ident; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags); + } + return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; +} + +static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return am->getResources().getTableCount(); +} + +static jint android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, + jint block) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return (jint)am->getResources().getTableStringBlock(block); +} + +static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, + jint cookie) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + String8 name(am->getAssetPath((void*)cookie)); + if (name.length() == 0) { + doThrow(env, "java/lang/IndexOutOfBoundsException"); + return NULL; + } + jstring str = env->NewStringUTF(name.string()); + if (str == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + return NULL; + } + return str; +} + +static jint android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return (jint)(new ResTable::Theme(am->getResources())); +} + +static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, + jint themeInt) +{ + ResTable::Theme* theme = (ResTable::Theme*)themeInt; + delete theme; +} + +static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, + jint themeInt, + jint styleRes, + jboolean force) +{ + ResTable::Theme* theme = (ResTable::Theme*)themeInt; + theme->applyStyle(styleRes, force ? true : false); +} + +static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, + jint destInt, jint srcInt) +{ + ResTable::Theme* dest = (ResTable::Theme*)destInt; + ResTable::Theme* src = (ResTable::Theme*)srcInt; + dest->setTo(*src); +} + +static jint android_content_AssetManager_loadThemeAttributeValue( + JNIEnv* env, jobject clazz, jint themeInt, jint ident, jobject outValue, jboolean resolve) +{ + ResTable::Theme* theme = (ResTable::Theme*)themeInt; + const ResTable& res(theme->getResTable()); + + Res_value value; + // XXX value could be different in different configs! + uint32_t typeSpecFlags = 0; + ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); + uint32_t ref = 0; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags); + } + return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; +} + +static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, + jint themeInt, jint pri, + jstring tag, jstring prefix) +{ + ResTable::Theme* theme = (ResTable::Theme*)themeInt; + const ResTable& res(theme->getResTable()); + + if (tag == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return; + } + + const char* tag8 = env->GetStringUTFChars(tag, NULL); + const char* prefix8 = NULL; + if (prefix != NULL) { + prefix8 = env->GetStringUTFChars(prefix, NULL); + } + + // XXX Need to use params. + theme->dumpToLog(); + + if (prefix8 != NULL) { + env->ReleaseStringUTFChars(prefix, prefix8); + } + env->ReleaseStringUTFChars(tag, tag8); +} + +static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject clazz, + jint themeToken, + jint defStyleAttr, + jint defStyleRes, + jint xmlParserToken, + jintArray attrs, + jintArray outValues, + jintArray outIndices) +{ + if (themeToken == 0 || attrs == NULL || outValues == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return JNI_FALSE; + } + + ResTable::Theme* theme = (ResTable::Theme*)themeToken; + const ResTable& res = theme->getResTable(); + ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; + Res_value value; + + const jsize NI = env->GetArrayLength(attrs); + const jsize NV = env->GetArrayLength(outValues); + if (NV < (NI*STYLE_NUM_ENTRIES)) { + doThrow(env, "java/lang/IndexOutOfBoundsException"); + return JNI_FALSE; + } + + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + return JNI_FALSE; + } + + jint* dest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (dest == NULL) { + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + doThrow(env, "java/lang/OutOfMemoryError"); + return JNI_FALSE; + } + + jint* indices = NULL; + int indicesIdx = 0; + if (outIndices != NULL) { + if (env->GetArrayLength(outIndices) > NI) { + indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); + } + } + + // Load default style from attribute, if specified... + uint32_t defStyleBagTypeSetFlags = 0; + if (defStyleAttr != 0) { + Res_value value; + if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) { + if (value.dataType == Res_value::TYPE_REFERENCE) { + defStyleRes = value.data; + } + } + } + + // Retrieve the style class associated with the current XML tag. + int style = 0; + uint32_t styleBagTypeSetFlags = 0; + if (xmlParser != NULL) { + ssize_t idx = xmlParser->indexOfStyle(); + if (idx >= 0 && xmlParser->getAttributeValue(idx, &value) >= 0) { + if (value.dataType == value.TYPE_ATTRIBUTE) { + if (theme->getAttribute(value.data, &value, &styleBagTypeSetFlags) < 0) { + value.dataType = Res_value::TYPE_NULL; + } + } + if (value.dataType == value.TYPE_REFERENCE) { + style = value.data; + } + } + } + + // Now lock down the resource object and start pulling stuff from it. + res.lock(); + + // Retrieve the default style bag, if requested. + const ResTable::bag_entry* defStyleEnt = NULL; + uint32_t defStyleTypeSetFlags = 0; + ssize_t bagOff = defStyleRes != 0 + ? res.getBagLocked(defStyleRes, &defStyleEnt, &defStyleTypeSetFlags) : -1; + defStyleTypeSetFlags |= defStyleBagTypeSetFlags; + const ResTable::bag_entry* endDefStyleEnt = defStyleEnt + + (bagOff >= 0 ? bagOff : 0); + + // Retrieve the style class bag, if requested. + const ResTable::bag_entry* styleEnt = NULL; + uint32_t styleTypeSetFlags = 0; + bagOff = style != 0 ? res.getBagLocked(style, &styleEnt, &styleTypeSetFlags) : -1; + styleTypeSetFlags |= styleBagTypeSetFlags; + const ResTable::bag_entry* endStyleEnt = styleEnt + + (bagOff >= 0 ? bagOff : 0); + + // Retrieve the XML attributes, if requested. + const jsize NX = xmlParser ? xmlParser->getAttributeCount() : 0; + jsize ix=0; + uint32_t curXmlAttr = xmlParser ? xmlParser->getAttributeNameResID(ix) : 0; + + static const ssize_t kXmlBlock = 0x10000000; + + // Now iterate through all of the attributes that the client has requested, + // filling in each with whatever data we can find. + ssize_t block = 0; + uint32_t typeSetFlags; + for (jsize ii=0; ii<NI; ii++) { + const uint32_t curIdent = (uint32_t)src[ii]; + + // Try to find a value for this attribute... we prioritize values + // coming from, first XML attributes, then XML style, then default + // style, and finally the theme. + value.dataType = Res_value::TYPE_NULL; + value.data = 0; + typeSetFlags = 0; + + // Skip through XML attributes until the end or the next possible match. + while (ix < NX && curIdent > curXmlAttr) { + ix++; + curXmlAttr = xmlParser->getAttributeNameResID(ix); + } + // Retrieve the current XML attribute if it matches, and step to next. + if (ix < NX && curIdent == curXmlAttr) { + block = kXmlBlock; + xmlParser->getAttributeValue(ix, &value); + ix++; + curXmlAttr = xmlParser->getAttributeNameResID(ix); + } + + // Skip through the style values until the end or the next possible match. + while (styleEnt < endStyleEnt && curIdent > styleEnt->map.name.ident) { + styleEnt++; + } + // Retrieve the current style attribute if it matches, and step to next. + if (styleEnt < endStyleEnt && curIdent == styleEnt->map.name.ident) { + if (value.dataType == Res_value::TYPE_NULL) { + block = styleEnt->stringBlock; + typeSetFlags = styleTypeSetFlags; + value = styleEnt->map.value; + } + styleEnt++; + } + + // Skip through the default style values until the end or the next possible match. + while (defStyleEnt < endDefStyleEnt && curIdent > defStyleEnt->map.name.ident) { + defStyleEnt++; + } + // Retrieve the current default style attribute if it matches, and step to next. + if (defStyleEnt < endDefStyleEnt && curIdent == defStyleEnt->map.name.ident) { + if (value.dataType == Res_value::TYPE_NULL) { + block = defStyleEnt->stringBlock; + typeSetFlags = defStyleTypeSetFlags; + value = defStyleEnt->map.value; + } + defStyleEnt++; + } + + //printf("Attribute 0x%08x: type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); + uint32_t resid = 0; + if (value.dataType != Res_value::TYPE_NULL) { + // Take care of resolving the found resource to its final value. + //printf("Resolving attribute reference\n"); + ssize_t newBlock = theme->resolveAttributeReference(&value, block, &resid, &typeSetFlags); + if (newBlock >= 0) block = newBlock; + } else { + // If we still don't have a value for this attribute, try to find + // it in the theme! + //printf("Looking up in theme\n"); + ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags); + if (newBlock >= 0) { + //printf("Resolving resource reference\n"); + newBlock = res.resolveReference(&value, block, &resid, &typeSetFlags); + if (newBlock >= 0) block = newBlock; + } + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + } + + //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); + + // Write the final value back to Java. + dest[STYLE_TYPE] = value.dataType; + dest[STYLE_DATA] = value.data; + dest[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? (jint)res.getTableCookie(block) : (jint)-1; + dest[STYLE_RESOURCE_ID] = resid; + dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; + + if (indices != NULL && value.dataType != Res_value::TYPE_NULL) { + indicesIdx++; + indices[indicesIdx] = ii; + } + + dest += STYLE_NUM_ENTRIES; + } + + res.unlock(); + + if (indices != NULL) { + indices[0] = indicesIdx; + env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); + } + env->ReleasePrimitiveArrayCritical(outValues, dest, 0); + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + + return JNI_TRUE; +} + +static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, + jint xmlParserToken, + jintArray attrs, + jintArray outValues, + jintArray outIndices) +{ + if (xmlParserToken == 0 || attrs == NULL || outValues == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + const ResTable& res(am->getResources()); + ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; + Res_value value; + + const jsize NI = env->GetArrayLength(attrs); + const jsize NV = env->GetArrayLength(outValues); + if (NV < (NI*STYLE_NUM_ENTRIES)) { + doThrow(env, "java/lang/IndexOutOfBoundsException"); + return JNI_FALSE; + } + + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + return JNI_FALSE; + } + + jint* dest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (dest == NULL) { + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + doThrow(env, "java/lang/OutOfMemoryError"); + return JNI_FALSE; + } + + jint* indices = NULL; + int indicesIdx = 0; + if (outIndices != NULL) { + if (env->GetArrayLength(outIndices) > NI) { + indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); + } + } + + // Now lock down the resource object and start pulling stuff from it. + res.lock(); + + // Retrieve the XML attributes, if requested. + const jsize NX = xmlParser->getAttributeCount(); + jsize ix=0; + uint32_t curXmlAttr = xmlParser->getAttributeNameResID(ix); + + static const ssize_t kXmlBlock = 0x10000000; + + // Now iterate through all of the attributes that the client has requested, + // filling in each with whatever data we can find. + ssize_t block = 0; + uint32_t typeSetFlags; + for (jsize ii=0; ii<NI; ii++) { + const uint32_t curIdent = (uint32_t)src[ii]; + + // Try to find a value for this attribute... + value.dataType = Res_value::TYPE_NULL; + value.data = 0; + typeSetFlags = 0; + + // Skip through XML attributes until the end or the next possible match. + while (ix < NX && curIdent > curXmlAttr) { + ix++; + curXmlAttr = xmlParser->getAttributeNameResID(ix); + } + // Retrieve the current XML attribute if it matches, and step to next. + if (ix < NX && curIdent == curXmlAttr) { + block = kXmlBlock; + xmlParser->getAttributeValue(ix, &value); + ix++; + curXmlAttr = xmlParser->getAttributeNameResID(ix); + } + + //printf("Attribute 0x%08x: type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); + uint32_t resid = 0; + if (value.dataType != Res_value::TYPE_NULL) { + // Take care of resolving the found resource to its final value. + //printf("Resolving attribute reference\n"); + ssize_t newBlock = res.resolveReference(&value, block, &resid, &typeSetFlags); + if (newBlock >= 0) block = newBlock; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + } + + //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); + + // Write the final value back to Java. + dest[STYLE_TYPE] = value.dataType; + dest[STYLE_DATA] = value.data; + dest[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? (jint)res.getTableCookie(block) : (jint)-1; + dest[STYLE_RESOURCE_ID] = resid; + dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; + + if (indices != NULL && value.dataType != Res_value::TYPE_NULL) { + indicesIdx++; + indices[indicesIdx] = ii; + } + + dest += STYLE_NUM_ENTRIES; + } + + res.unlock(); + + if (indices != NULL) { + indices[0] = indicesIdx; + env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); + } + + env->ReleasePrimitiveArrayCritical(outValues, dest, 0); + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + + return JNI_TRUE; +} + +static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, + jint id) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + res.lock(); + const ResTable::bag_entry* defStyleEnt = NULL; + ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); + res.unlock(); + + return bagOff; +} + +static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, + jint id, + jintArray outValues) +{ + if (outValues == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + const ResTable& res(am->getResources()); + Res_value value; + ssize_t block; + + const jsize NV = env->GetArrayLength(outValues); + + jint* dest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (dest == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + return JNI_FALSE; + } + + // Now lock down the resource object and start pulling stuff from it. + res.lock(); + + const ResTable::bag_entry* arrayEnt = NULL; + uint32_t arrayTypeSetFlags = 0; + ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); + const ResTable::bag_entry* endArrayEnt = arrayEnt + + (bagOff >= 0 ? bagOff : 0); + + int i = 0; + uint32_t typeSetFlags; + while (i < NV && arrayEnt < endArrayEnt) { + block = arrayEnt->stringBlock; + typeSetFlags = arrayTypeSetFlags; + value = arrayEnt->map.value; + + uint32_t resid = 0; + if (value.dataType != Res_value::TYPE_NULL) { + // Take care of resolving the found resource to its final value. + //printf("Resolving attribute reference\n"); + ssize_t newBlock = res.resolveReference(&value, block, &resid, &typeSetFlags); + if (newBlock >= 0) block = newBlock; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + } + + //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); + + // Write the final value back to Java. + dest[STYLE_TYPE] = value.dataType; + dest[STYLE_DATA] = value.data; + dest[STYLE_ASSET_COOKIE] = (jint)res.getTableCookie(block); + dest[STYLE_RESOURCE_ID] = resid; + dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; + dest += STYLE_NUM_ENTRIES; + i+= STYLE_NUM_ENTRIES; + arrayEnt++; + } + + i /= STYLE_NUM_ENTRIES; + + res.unlock(); + + env->ReleasePrimitiveArrayCritical(outValues, dest, 0); + + return i; +} + +static jint android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + LOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); + + if (fileName == NULL || am == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + const char* fileName8 = env->GetStringUTFChars(fileName, NULL); + Asset* a = cookie + ? am->openNonAsset((void*)cookie, fileName8, Asset::ACCESS_BUFFER) + : am->openNonAsset(fileName8, Asset::ACCESS_BUFFER); + + if (a == NULL) { + doThrow(env, "java/io/FileNotFoundException", fileName8); + env->ReleaseStringUTFChars(fileName, fileName8); + return 0; + } + env->ReleaseStringUTFChars(fileName, fileName8); + + ResXMLTree* block = new ResXMLTree(); + status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); + a->close(); + delete a; + + if (err != NO_ERROR) { + doThrow(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + + return (jint)block; +} + +static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N * 2); + if (array == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) { + jint stringIndex = -1; + jint stringBlock = 0; + value = bag->map.value; + + // Take care of resolving the found resource to its final value. + stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); + if (value.dataType == Res_value::TYPE_STRING) { + stringIndex = value.data; + } + + //todo: It might be faster to allocate a C array to contain + // the blocknums and indices, put them in there and then + // do just one SetIntArrayRegion() + env->SetIntArrayRegion(array, j, 1, &stringBlock); + env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); + j = j + 2; + } + res.unlockBag(startOfBag); + return array; +} + +static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + jclass cls = env->FindClass("java/lang/String"); + LOG_FATAL_IF(cls == NULL, "No string class?!?"); + if (cls == NULL) { + return NULL; + } + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jobjectArray array = env->NewObjectArray(N, cls, NULL); + if (array == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + size_t strLen = 0; + for (size_t i=0; ((ssize_t)i)<N; i++, bag++) { + value = bag->map.value; + jstring str = NULL; + + // Take care of resolving the found resource to its final value. + ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); + if (value.dataType == Res_value::TYPE_STRING) { + const char16_t* str16 = res.getTableStringBlock(block)->stringAt(value.data, &strLen); + str = env->NewString(str16, strLen); + if (str == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + res.unlockBag(startOfBag); + return NULL; + } + } + + env->SetObjectArrayElement(array, i, str); + } + res.unlockBag(startOfBag); + return array; +} + +static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N); + if (array == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + for (size_t i=0; ((ssize_t)i)<N; i++, bag++) { + value = bag->map.value; + + // Take care of resolving the found resource to its final value. + ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); + if (value.dataType >= Res_value::TYPE_FIRST_INT + && value.dataType <= Res_value::TYPE_LAST_INT) { + int intVal = value.data; + env->SetIntArrayRegion(array, i, 1, &intVal); + } + } + res.unlockBag(startOfBag); + return array; +} + +static void android_content_AssetManager_init(JNIEnv* env, jobject clazz) +{ + AssetManager* am = new AssetManager(); + if (am == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + return; + } + + am->addDefaultAssets(); + + LOGV("Created AssetManager %p for Java object %p\n", am, clazz); + env->SetIntField(clazz, gAssetManagerOffsets.mObject, (jint)am); +} + +static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) +{ + AssetManager* am = (AssetManager*) + (env->GetIntField(clazz, gAssetManagerOffsets.mObject)); + LOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); + if (am != NULL) { + delete am; + env->SetIntField(clazz, gAssetManagerOffsets.mObject, 0); + } +} + +static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) +{ + return Asset::getGlobalCount(); +} + +static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) +{ + return AssetManager::getGlobalCount(); +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gAssetManagerMethods[] = { + /* name, signature, funcPtr */ + + // Basic asset stuff. + { "openAsset", "(Ljava/lang/String;I)I", + (void*) android_content_AssetManager_openAsset }, + { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*) android_content_AssetManager_openAssetFd }, + { "openNonAssetNative", "(ILjava/lang/String;I)I", + (void*) android_content_AssetManager_openNonAssetNative }, + { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*) android_content_AssetManager_openNonAssetFdNative }, + { "list", "(Ljava/lang/String;)[Ljava/lang/String;", + (void*) android_content_AssetManager_list }, + { "destroyAsset", "(I)V", + (void*) android_content_AssetManager_destroyAsset }, + { "readAssetChar", "(I)I", + (void*) android_content_AssetManager_readAssetChar }, + { "readAsset", "(I[BII)I", + (void*) android_content_AssetManager_readAsset }, + { "seekAsset", "(IJI)J", + (void*) android_content_AssetManager_seekAsset }, + { "getAssetLength", "(I)J", + (void*) android_content_AssetManager_getAssetLength }, + { "getAssetRemainingLength", "(I)J", + (void*) android_content_AssetManager_getAssetRemainingLength }, + { "addAssetPath", "(Ljava/lang/String;)I", + (void*) android_content_AssetManager_addAssetPath }, + { "isUpToDate", "()Z", + (void*) android_content_AssetManager_isUpToDate }, + + // Resources. + { "setLocale", "(Ljava/lang/String;)V", + (void*) android_content_AssetManager_setLocale }, + { "getLocales", "()[Ljava/lang/String;", + (void*) android_content_AssetManager_getLocales }, + { "setConfiguration", "(IILjava/lang/String;IIIIIIIII)V", + (void*) android_content_AssetManager_setConfiguration }, + { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*) android_content_AssetManager_getResourceIdentifier }, + { "getResourceName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceName }, + { "getResourcePackageName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourcePackageName }, + { "getResourceTypeName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceTypeName }, + { "getResourceEntryName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceEntryName }, + { "loadResourceValue","(ILandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadResourceValue }, + { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadResourceBagValue }, + { "getStringBlockCount","()I", + (void*) android_content_AssetManager_getStringBlockCount }, + { "getNativeStringBlock","(I)I", + (void*) android_content_AssetManager_getNativeStringBlock }, + { "getCookieName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getCookieName }, + + // Themes. + { "newTheme", "()I", + (void*) android_content_AssetManager_newTheme }, + { "deleteTheme", "(I)V", + (void*) android_content_AssetManager_deleteTheme }, + { "applyThemeStyle", "(IIZ)V", + (void*) android_content_AssetManager_applyThemeStyle }, + { "copyTheme", "(II)V", + (void*) android_content_AssetManager_copyTheme }, + { "loadThemeAttributeValue", "(IILandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadThemeAttributeValue }, + { "dumpTheme", "(IILjava/lang/String;Ljava/lang/String;)V", + (void*) android_content_AssetManager_dumpTheme }, + { "applyStyle","(IIII[I[I[I)Z", + (void*) android_content_AssetManager_applyStyle }, + { "retrieveAttributes","(I[I[I[I)Z", + (void*) android_content_AssetManager_retrieveAttributes }, + { "getArraySize","(I)I", + (void*) android_content_AssetManager_getArraySize }, + { "retrieveArray","(I[I)I", + (void*) android_content_AssetManager_retrieveArray }, + + // XML files. + { "openXmlAssetNative", "(ILjava/lang/String;)I", + (void*) android_content_AssetManager_openXmlAssetNative }, + + // Arrays. + { "getArrayStringResource","(I)[Ljava/lang/String;", + (void*) android_content_AssetManager_getArrayStringResource }, + { "getArrayStringInfo","(I)[I", + (void*) android_content_AssetManager_getArrayStringInfo }, + { "getArrayIntResource","(I)[I", + (void*) android_content_AssetManager_getArrayIntResource }, + + // Bookkeeping. + { "init", "()V", + (void*) android_content_AssetManager_init }, + { "destroy", "()V", + (void*) android_content_AssetManager_destroy }, + { "getGlobalAssetCount", "()I", + (void*) android_content_AssetManager_getGlobalAssetCount }, + { "getGlobalAssetManagerCount", "()I", + (void*) android_content_AssetManager_getGlobalAssetCount }, +}; + +int register_android_content_AssetManager(JNIEnv* env) +{ + jclass typedValue = env->FindClass("android/util/TypedValue"); + LOG_FATAL_IF(typedValue == NULL, "Unable to find class android/util/TypedValue"); + gTypedValueOffsets.mType + = env->GetFieldID(typedValue, "type", "I"); + LOG_FATAL_IF(gTypedValueOffsets.mType == NULL, "Unable to find TypedValue.type"); + gTypedValueOffsets.mData + = env->GetFieldID(typedValue, "data", "I"); + LOG_FATAL_IF(gTypedValueOffsets.mData == NULL, "Unable to find TypedValue.data"); + gTypedValueOffsets.mString + = env->GetFieldID(typedValue, "string", "Ljava/lang/CharSequence;"); + LOG_FATAL_IF(gTypedValueOffsets.mString == NULL, "Unable to find TypedValue.string"); + gTypedValueOffsets.mAssetCookie + = env->GetFieldID(typedValue, "assetCookie", "I"); + LOG_FATAL_IF(gTypedValueOffsets.mAssetCookie == NULL, "Unable to find TypedValue.assetCookie"); + gTypedValueOffsets.mResourceId + = env->GetFieldID(typedValue, "resourceId", "I"); + LOG_FATAL_IF(gTypedValueOffsets.mResourceId == NULL, "Unable to find TypedValue.resourceId"); + gTypedValueOffsets.mChangingConfigurations + = env->GetFieldID(typedValue, "changingConfigurations", "I"); + LOG_FATAL_IF(gTypedValueOffsets.mChangingConfigurations == NULL, "Unable to find TypedValue.changingConfigurations"); + + jclass assetFd = env->FindClass("android/content/res/AssetFileDescriptor"); + LOG_FATAL_IF(assetFd == NULL, "Unable to find class android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd + = env->GetFieldID(assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); + LOG_FATAL_IF(gAssetFileDescriptorOffsets.mFd == NULL, "Unable to find AssetFileDescriptor.mFd"); + gAssetFileDescriptorOffsets.mStartOffset + = env->GetFieldID(assetFd, "mStartOffset", "J"); + LOG_FATAL_IF(gAssetFileDescriptorOffsets.mStartOffset == NULL, "Unable to find AssetFileDescriptor.mStartOffset"); + gAssetFileDescriptorOffsets.mLength + = env->GetFieldID(assetFd, "mLength", "J"); + LOG_FATAL_IF(gAssetFileDescriptorOffsets.mLength == NULL, "Unable to find AssetFileDescriptor.mLength"); + + jclass assetManager = env->FindClass("android/content/res/AssetManager"); + LOG_FATAL_IF(assetManager == NULL, "Unable to find class android/content/res/AssetManager"); + gAssetManagerOffsets.mObject + = env->GetFieldID(assetManager, "mObject", "I"); + LOG_FATAL_IF(gAssetManagerOffsets.mObject == NULL, "Unable to find AssetManager.mObject"); + + g_stringClass = env->FindClass("java/lang/String"); + + return AndroidRuntime::registerNativeMethods(env, + "android/content/res/AssetManager", gAssetManagerMethods, NELEM(gAssetManagerMethods)); +} + +}; // namespace android diff --git a/core/jni/android_util_Base64.cpp b/core/jni/android_util_Base64.cpp new file mode 100644 index 0000000..bc69747 --- /dev/null +++ b/core/jni/android_util_Base64.cpp @@ -0,0 +1,160 @@ +/* //device/libs/android_runtime/android_util_Base64.cpp +** +** 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. +*/ + +/********************************************************* +* +* This code was copied from +* system/extra/ssh/dropbear-0.49/libtomcrypt/src/misc/base64/base64_decode.c +* +*********************************************************/ + +#define LOG_TAG "Base64" + +#include <utils/Log.h> + +#include <android_runtime/AndroidRuntime.h> + +#include "JNIHelp.h" + +#include <sys/errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <signal.h> + +namespace android { + +static const unsigned char map[256] = { +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, +255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, +255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255 }; + +/** + base64 decode a block of memory + @param in The base64 data to decode + @param inlen The length of the base64 data + @param out [out] The destination of the binary decoded data + @param outlen [in/out] The max size and resulting size of the decoded data + @return 0 if successful +*/ +int base64_decode(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) +{ + unsigned long t, x, y, z; + unsigned char c; + int g; + + g = 3; + for (x = y = z = t = 0; x < inlen; x++) { + c = map[in[x]&0xFF]; + if (c == 255) continue; + /* the final = symbols are read and used to trim the remaining bytes */ + if (c == 254) { + c = 0; + /* prevent g < 0 which would potentially allow an overflow later */ + if (--g < 0) { + return -3; + } + } else if (g != 3) { + /* we only allow = to be at the end */ + return -4; + } + + t = (t<<6)|c; + + if (++y == 4) { + if (z + g > *outlen) { + return -2; + } + out[z++] = (unsigned char)((t>>16)&255); + if (g > 1) out[z++] = (unsigned char)((t>>8)&255); + if (g > 2) out[z++] = (unsigned char)(t&255); + y = t = 0; + } + } + if (y != 0) { + return -5; + } + *outlen = z; + return 0; +} + +static jbyteArray decodeBase64(JNIEnv *env, jobject jobj, jstring jdata) +{ + const char * rawData = env->GetStringUTFChars(jdata, NULL); + int stringLength = env->GetStringUTFLength(jdata); + + int resultLength = stringLength / 4 * 3; + if (rawData[stringLength-1] == '=') { + resultLength -= 1; + if (rawData[stringLength-2] == '=') { + resultLength -= 1; + } + } + + jbyteArray byteArray = env->NewByteArray(resultLength); + jbyte* byteArrayData = env->GetByteArrayElements(byteArray, NULL); + + unsigned long outlen = resultLength; + int result = base64_decode((const unsigned char*)rawData, stringLength, (unsigned char *)byteArrayData, &outlen); + if (result != 0) + memset((unsigned char *)byteArrayData, -result, resultLength); + + env->ReleaseStringUTFChars(jdata, rawData); + env->ReleaseByteArrayElements(byteArray, byteArrayData, 0); + + return byteArray; +} + +static const JNINativeMethod methods[] = { + {"decodeBase64Native", "(Ljava/lang/String;)[B", (void*)decodeBase64 } +}; + +static const char* const kBase64PathName = "android/os/Base64Utils"; + +int register_android_util_Base64(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass(kBase64PathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Base64Utils"); + + return AndroidRuntime::registerNativeMethods( + env, kBase64PathName, + methods, NELEM(methods)); +} + +} diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp new file mode 100644 index 0000000..24404a8 --- /dev/null +++ b/core/jni/android_util_Binder.cpp @@ -0,0 +1,1510 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "JavaBinder" +//#define LOG_NDEBUG 0 + +#include "android_util_Binder.h" +#include "JNIHelp.h" + +#include <fcntl.h> +#include <sys/stat.h> +#include <stdio.h> + +#include <utils/Atomic.h> +#include <utils/IInterface.h> +#include <utils/IPCThreadState.h> +#include <utils/Log.h> +#include <utils/Parcel.h> +#include <utils/ProcessState.h> +#include <utils/IServiceManager.h> + +#include <android_runtime/AndroidRuntime.h> + +//#undef LOGV +//#define LOGV(...) fprintf(stderr, __VA_ARGS__) + +using namespace android; + +// ---------------------------------------------------------------------------- + +static struct bindernative_offsets_t +{ + // Class state. + jclass mClass; + jmethodID mExecTransact; + + // Object state. + jfieldID mObject; + +} gBinderOffsets; + +// ---------------------------------------------------------------------------- + +static struct binderinternal_offsets_t +{ + // Class state. + jclass mClass; + jmethodID mForceGc; + +} gBinderInternalOffsets; + +// ---------------------------------------------------------------------------- + +static struct debug_offsets_t +{ + // Class state. + jclass mClass; + +} gDebugOffsets; + +// ---------------------------------------------------------------------------- + +static struct weakreference_offsets_t +{ + // Class state. + jclass mClass; + jmethodID mGet; + +} gWeakReferenceOffsets; + +static struct error_offsets_t +{ + jclass mClass; +} gErrorOffsets; + +// ---------------------------------------------------------------------------- + +static struct binderproxy_offsets_t +{ + // Class state. + jclass mClass; + jmethodID mConstructor; + jmethodID mSendDeathNotice; + + // Object state. + jfieldID mObject; + jfieldID mSelf; + +} gBinderProxyOffsets; + +// ---------------------------------------------------------------------------- + +static struct parcel_offsets_t +{ + jfieldID mObject; + jfieldID mOwnObject; +} gParcelOffsets; + +static struct log_offsets_t +{ + // Class state. + jclass mClass; + jmethodID mLogE; +} gLogOffsets; + +static struct file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; + jfieldID mDescriptor; +} gFileDescriptorOffsets; + +static struct parcel_file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; +} gParcelFileDescriptorOffsets; + +// **************************************************************************** +// **************************************************************************** +// **************************************************************************** + +static volatile int32_t gNumRefsCreated = 0; +static volatile int32_t gNumProxyRefs = 0; +static volatile int32_t gNumLocalRefs = 0; +static volatile int32_t gNumDeathRefs = 0; + +static void incRefsCreated(JNIEnv* env) +{ + int old = android_atomic_inc(&gNumRefsCreated); + if (old == 200) { + android_atomic_and(0, &gNumRefsCreated); + env->CallStaticVoidMethod(gBinderInternalOffsets.mClass, + gBinderInternalOffsets.mForceGc); + } else { + LOGV("Now have %d binder ops", old); + } +} + +static JavaVM* jnienv_to_javavm(JNIEnv* env) +{ + JavaVM* vm; + return env->GetJavaVM(&vm) >= 0 ? vm : NULL; +} + +static JNIEnv* javavm_to_jnienv(JavaVM* vm) +{ + JNIEnv* env; + return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL; +} + +static void report_exception(JNIEnv* env, jthrowable excep, const char* msg) +{ + env->ExceptionClear(); + + jstring tagstr = env->NewStringUTF(LOG_TAG); + jstring msgstr = env->NewStringUTF(msg); + + if ((tagstr == NULL) || (msgstr == NULL)) { + env->ExceptionClear(); /* assume exception (OOM?) was thrown */ + LOGE("Unable to call Log.e()\n"); + LOGE("%s", msg); + goto bail; + } + + env->CallStaticIntMethod( + gLogOffsets.mClass, gLogOffsets.mLogE, tagstr, msgstr, excep); + if (env->ExceptionCheck()) { + /* attempting to log the failure has failed */ + LOGW("Failed trying to log exception, msg='%s'\n", msg); + env->ExceptionClear(); + } + + if (env->IsInstanceOf(excep, gErrorOffsets.mClass)) { + /* + * It's an Error: Reraise the exception, detach this thread, and + * wait for the fireworks. Die even more blatantly after a minute + * if the gentler attempt doesn't do the trick. + * + * The GetJavaVM function isn't on the "approved" list of JNI calls + * that can be made while an exception is pending, so we want to + * get the VM ptr, throw the exception, and then detach the thread. + */ + JavaVM* vm = jnienv_to_javavm(env); + env->Throw(excep); + vm->DetachCurrentThread(); + sleep(60); + LOGE("Forcefully exiting"); + exit(1); + *((int *) 1) = 1; + } + +bail: + /* discard local refs created for us by VM */ + env->DeleteLocalRef(tagstr); + env->DeleteLocalRef(msgstr); +} + +class JavaBBinderHolder; + +class JavaBBinder : public BBinder +{ +public: + JavaBBinder(JNIEnv* env, jobject object) + : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)) + { + LOGV("Creating JavaBBinder %p\n", this); + android_atomic_inc(&gNumLocalRefs); + incRefsCreated(env); + } + + bool checkSubclass(const void* subclassID) const + { + return subclassID == &gBinderOffsets; + } + + jobject object() const + { + return mObject; + } + +protected: + virtual ~JavaBBinder() + { + LOGV("Destroying JavaBBinder %p\n", this); + android_atomic_dec(&gNumLocalRefs); + JNIEnv* env = javavm_to_jnienv(mVM); + env->DeleteGlobalRef(mObject); + } + + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) + { + JNIEnv* env = javavm_to_jnienv(mVM); + + LOGV("onTransact() on %p calling object %p in env %p vm %p\n", this, mObject, env, mVM); + + //printf("Transact from %p to Java code sending: ", this); + //data.print(); + //printf("\n"); + jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact, + code, (int32_t)&data, (int32_t)reply, flags); + jthrowable excep = env->ExceptionOccurred(); + if (excep) { + report_exception(env, excep, + "*** Uncaught remote exception! " + "(Exceptions are not yet supported across processes.)"); + res = JNI_FALSE; + + /* clean up JNI local ref -- we don't return to Java code */ + env->DeleteLocalRef(excep); + } + + //aout << "onTransact to Java code; result=" << res << endl + // << "Transact from " << this << " to Java code returning " + // << reply << ": " << *reply << endl; + return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION; + } + + virtual status_t dump(int fd, const Vector<String16>& args) + { + return 0; + } + +private: + JavaVM* const mVM; + jobject const mObject; +}; + +// ---------------------------------------------------------------------------- + +class JavaBBinderHolder : public RefBase +{ +public: + JavaBBinderHolder(JNIEnv* env, jobject object) + : mObject(object) + { + LOGV("Creating JavaBBinderHolder for Object %p\n", object); + } + ~JavaBBinderHolder() + { + LOGV("Destroying JavaBBinderHolder for Object %p\n", mObject); + } + + sp<JavaBBinder> get(JNIEnv* env) + { + AutoMutex _l(mLock); + sp<JavaBBinder> b = mBinder.promote(); + if (b == NULL) { + b = new JavaBBinder(env, mObject); + mBinder = b; + LOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%d\n", + b.get(), b->getWeakRefs(), mObject, b->getWeakRefs()->getWeakCount()); + } + + return b; + } + + sp<JavaBBinder> getExisting() + { + AutoMutex _l(mLock); + return mBinder.promote(); + } + +private: + Mutex mLock; + jobject mObject; + wp<JavaBBinder> mBinder; +}; + +// ---------------------------------------------------------------------------- + +class JavaDeathRecipient : public IBinder::DeathRecipient +{ +public: + JavaDeathRecipient(JNIEnv* env, jobject object) + : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)), + mHoldsRef(true) + { + incStrong(this); + android_atomic_inc(&gNumDeathRefs); + incRefsCreated(env); + } + + void binderDied(const wp<IBinder>& who) + { + JNIEnv* env = javavm_to_jnienv(mVM); + + LOGV("Receiving binderDied() on JavaDeathRecipient %p\n", this); + + env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, + gBinderProxyOffsets.mSendDeathNotice, mObject); + jthrowable excep = env->ExceptionOccurred(); + if (excep) { + report_exception(env, excep, + "*** Uncaught exception returned from death notification!"); + } + + clearReference(); + } + + void clearReference() + { + bool release = false; + mLock.lock(); + if (mHoldsRef) { + mHoldsRef = false; + release = true; + } + mLock.unlock(); + if (release) { + decStrong(this); + } + } + +protected: + virtual ~JavaDeathRecipient() + { + //LOGI("Removing death ref: recipient=%p\n", mObject); + android_atomic_dec(&gNumDeathRefs); + JNIEnv* env = javavm_to_jnienv(mVM); + env->DeleteGlobalRef(mObject); + } + +private: + JavaVM* const mVM; + jobject const mObject; + Mutex mLock; + bool mHoldsRef; +}; + +// ---------------------------------------------------------------------------- + +namespace android { + +static void proxy_cleanup(const void* id, void* obj, void* cleanupCookie) +{ + android_atomic_dec(&gNumProxyRefs); + JNIEnv* env = javavm_to_jnienv((JavaVM*)cleanupCookie); + env->DeleteGlobalRef((jobject)obj); +} + +static Mutex mProxyLock; + +jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) +{ + if (val == NULL) return NULL; + + if (val->checkSubclass(&gBinderOffsets)) { + // One of our own! + jobject object = static_cast<JavaBBinder*>(val.get())->object(); + //printf("objectForBinder %p: it's our own %p!\n", val.get(), object); + return object; + } + + // For the rest of the function we will hold this lock, to serialize + // looking/creation of Java proxies for native Binder proxies. + AutoMutex _l(mProxyLock); + + // Someone else's... do we know about it? + jobject object = (jobject)val->findObject(&gBinderProxyOffsets); + if (object != NULL) { + jobject res = env->CallObjectMethod(object, gWeakReferenceOffsets.mGet); + if (res != NULL) { + LOGV("objectForBinder %p: found existing %p!\n", val.get(), res); + return res; + } + LOGV("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get()); + android_atomic_dec(&gNumProxyRefs); + val->detachObject(&gBinderProxyOffsets); + env->DeleteGlobalRef(object); + } + + object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor); + if (object != NULL) { + LOGV("objectForBinder %p: created new %p!\n", val.get(), object); + // The proxy holds a reference to the native object. + env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get()); + val->incStrong(object); + + // The native object needs to hold a weak reference back to the + // proxy, so we can retrieve the same proxy if it is still active. + jobject refObject = env->NewGlobalRef( + env->GetObjectField(object, gBinderProxyOffsets.mSelf)); + val->attachObject(&gBinderProxyOffsets, refObject, + jnienv_to_javavm(env), proxy_cleanup); + + // Note that a new object reference has been created. + android_atomic_inc(&gNumProxyRefs); + incRefsCreated(env); + } + + return object; +} + +sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj) +{ + if (obj == NULL) return NULL; + + if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) { + JavaBBinderHolder* jbh = (JavaBBinderHolder*) + env->GetIntField(obj, gBinderOffsets.mObject); + return jbh != NULL ? jbh->get(env) : NULL; + } + + if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) { + return (IBinder*) + env->GetIntField(obj, gBinderProxyOffsets.mObject); + } + + LOGW("ibinderForJavaObject: %p is not a Binder object", obj); + return NULL; +} + +Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) +{ + if (obj) { + Parcel* p = (Parcel*)env->GetIntField(obj, gParcelOffsets.mObject); + if (p != NULL) { + return p; + } + jniThrowException(env, "java/lang/IllegalStateException", "Parcel has been finalized!"); + } + return NULL; +} + +jobject newFileDescriptor(JNIEnv* env, int fd) +{ + jobject object = env->NewObject( + gFileDescriptorOffsets.mClass, gFileDescriptorOffsets.mConstructor); + if (object != NULL) { + //LOGI("Created new FileDescriptor %p with fd %d\n", object, fd); + env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, fd); + } + return object; +} + +jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc) +{ + return env->NewObject( + gParcelFileDescriptorOffsets.mClass, gParcelFileDescriptorOffsets.mConstructor, fileDesc); +} + +void signalExceptionForError(JNIEnv* env, jobject obj, status_t err) +{ + switch (err) { + case UNKNOWN_ERROR: + jniThrowException(env, "java/lang/RuntimeException", "Unknown error"); + break; + case NO_MEMORY: + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + break; + case INVALID_OPERATION: + jniThrowException(env, "java/lang/UnsupportedOperationException", NULL); + break; + case BAD_VALUE: + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + break; + case BAD_INDEX: + jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL); + break; + case BAD_TYPE: + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + break; + case NAME_NOT_FOUND: + jniThrowException(env, "java/util/NoSuchElementException", NULL); + break; + case PERMISSION_DENIED: + jniThrowException(env, "java/lang/SecurityException", NULL); + break; + case NOT_ENOUGH_DATA: + jniThrowException(env, "android/os/ParcelFormatException", "Not enough data"); + break; + case NO_INIT: + jniThrowException(env, "java/lang/RuntimeException", "Not initialized"); + break; + case ALREADY_EXISTS: + jniThrowException(env, "java/lang/RuntimeException", "Item already exists"); + break; + case DEAD_OBJECT: + jniThrowException(env, "android/os/DeadObjectException", NULL); + break; + case UNKNOWN_TRANSACTION: + jniThrowException(env, "java/lang/RuntimeException", "Unknown transaction code"); + break; + case FAILED_TRANSACTION: + LOGE("!!! FAILED BINDER TRANSACTION !!!"); + //jniThrowException(env, "java/lang/OutOfMemoryError", "Binder transaction too large"); + break; + default: + LOGE("Unknown binder error code. 0x%x", err); + } +} + +} + +// ---------------------------------------------------------------------------- + +static jint android_os_Binder_getCallingPid(JNIEnv* env, jobject clazz) +{ + return IPCThreadState::self()->getCallingPid(); +} + +static jint android_os_Binder_getCallingUid(JNIEnv* env, jobject clazz) +{ + return IPCThreadState::self()->getCallingUid(); +} + +static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz) +{ + return IPCThreadState::self()->clearCallingIdentity(); +} + +static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token) +{ + IPCThreadState::self()->restoreCallingIdentity(token); +} + +static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz) +{ + IPCThreadState::self()->flushCommands(); +} + +static void android_os_Binder_init(JNIEnv* env, jobject clazz) +{ + JavaBBinderHolder* jbh = new JavaBBinderHolder(env, clazz); + if (jbh == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return; + } + LOGV("Java Binder %p: acquiring first ref on holder %p", clazz, jbh); + jbh->incStrong(clazz); + env->SetIntField(clazz, gBinderOffsets.mObject, (int)jbh); +} + +static void android_os_Binder_destroy(JNIEnv* env, jobject clazz) +{ + JavaBBinderHolder* jbh = (JavaBBinderHolder*) + env->GetIntField(clazz, gBinderOffsets.mObject); + env->SetIntField(clazz, gBinderOffsets.mObject, 0); + LOGV("Java Binder %p: removing ref on holder %p", clazz, jbh); + jbh->decStrong(clazz); +} + +// ---------------------------------------------------------------------------- + +static const JNINativeMethod gBinderMethods[] = { + /* name, signature, funcPtr */ + { "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid }, + { "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid }, + { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity }, + { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity }, + { "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands }, + { "init", "()V", (void*)android_os_Binder_init }, + { "destroy", "()V", (void*)android_os_Binder_destroy } +}; + +const char* const kBinderPathName = "android/os/Binder"; + +static int int_register_android_os_Binder(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass(kBinderPathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Binder"); + + gBinderOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gBinderOffsets.mExecTransact + = env->GetMethodID(clazz, "execTransact", "(IIII)Z"); + assert(gBinderOffsets.mExecTransact); + + gBinderOffsets.mObject + = env->GetFieldID(clazz, "mObject", "I"); + assert(gBinderOffsets.mObject); + + return AndroidRuntime::registerNativeMethods( + env, kBinderPathName, + gBinderMethods, NELEM(gBinderMethods)); +} + +// **************************************************************************** +// **************************************************************************** +// **************************************************************************** + +namespace android { + +jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz) +{ + return gNumLocalRefs; +} + +jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz) +{ + return gNumProxyRefs; +} + +jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz) +{ + return gNumDeathRefs; +} + +} + +// **************************************************************************** +// **************************************************************************** +// **************************************************************************** + +static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz) +{ + sp<IBinder> b = ProcessState::self()->getContextObject(NULL); + return javaObjectForIBinder(env, b); +} + +static void android_os_BinderInternal_joinThreadPool(JNIEnv* env, jobject clazz) +{ + sp<IBinder> b = ProcessState::self()->getContextObject(NULL); + android::IPCThreadState::self()->joinThreadPool(); +} + +static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz) +{ + LOGV("Gc has executed, clearing binder ops"); + android_atomic_and(0, &gNumRefsCreated); +} + +// ---------------------------------------------------------------------------- + +static const JNINativeMethod gBinderInternalMethods[] = { + /* name, signature, funcPtr */ + { "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject }, + { "joinThreadPool", "()V", (void*)android_os_BinderInternal_joinThreadPool }, + { "handleGc", "()V", (void*)android_os_BinderInternal_handleGc } +}; + +const char* const kBinderInternalPathName = "com/android/internal/os/BinderInternal"; + +static int int_register_android_os_BinderInternal(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass(kBinderInternalPathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class com.android.internal.os.BinderInternal"); + + gBinderInternalOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gBinderInternalOffsets.mForceGc + = env->GetStaticMethodID(clazz, "forceBinderGc", "()V"); + assert(gBinderInternalOffsets.mForceGc); + + return AndroidRuntime::registerNativeMethods( + env, kBinderInternalPathName, + gBinderInternalMethods, NELEM(gBinderInternalMethods)); +} + +// **************************************************************************** +// **************************************************************************** +// **************************************************************************** + +static jboolean android_os_BinderProxy_pingBinder(JNIEnv* env, jobject obj) +{ + IBinder* target = (IBinder*) + env->GetIntField(obj, gBinderProxyOffsets.mObject); + if (target == NULL) { + return JNI_FALSE; + } + status_t err = target->pingBinder(); + return err == NO_ERROR ? JNI_TRUE : JNI_FALSE; +} + +static jstring android_os_BinderProxy_getInterfaceDescriptor(JNIEnv* env, jobject obj) +{ + IBinder* target = (IBinder*) env->GetIntField(obj, gBinderProxyOffsets.mObject); + if (target != NULL) { + String16 desc = target->getInterfaceDescriptor(); + return env->NewString(desc.string(), desc.size()); + } + jniThrowException(env, "java/lang/RuntimeException", + "No binder found for object"); + return NULL; +} + +static jboolean android_os_BinderProxy_isBinderAlive(JNIEnv* env, jobject obj) +{ + IBinder* target = (IBinder*) + env->GetIntField(obj, gBinderProxyOffsets.mObject); + if (target == NULL) { + return JNI_FALSE; + } + bool alive = target->isBinderAlive(); + return alive ? JNI_TRUE : JNI_FALSE; +} + +static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, + jint code, jobject dataObj, + jobject replyObj, jint flags) +{ + if (dataObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return JNI_FALSE; + } + + Parcel* data = parcelForJavaObject(env, dataObj); + if (data == NULL) { + return JNI_FALSE; + } + Parcel* reply = parcelForJavaObject(env, replyObj); + if (reply == NULL && replyObj != NULL) { + return JNI_FALSE; + } + + IBinder* target = (IBinder*) + env->GetIntField(obj, gBinderProxyOffsets.mObject); + if (target == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!"); + return JNI_FALSE; + } + + LOGV("Java code calling transact on %p in Java object %p with code %d\n", + target, obj, code); + //printf("Transact from Java code to %p sending: ", target); data->print(); + status_t err = target->transact(code, *data, reply, flags); + //if (reply) printf("Transact from Java code to %p received: ", target); reply->print(); + if (err == NO_ERROR) { + return JNI_TRUE; + } else if (err == UNKNOWN_TRANSACTION) { + return JNI_FALSE; + } + + signalExceptionForError(env, obj, err); + return JNI_FALSE; +} + +static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj, + jobject recipient, jint flags) +{ + if (recipient == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + IBinder* target = (IBinder*) + env->GetIntField(obj, gBinderProxyOffsets.mObject); + if (target == NULL) { + LOGW("Binder has been finalized when calling linkToDeath() with recip=%p)\n", recipient); + assert(false); + } + + LOGV("linkToDeath: binder=%p recipient=%p\n", target, recipient); + + if (!target->localBinder()) { + sp<JavaDeathRecipient> jdr = new JavaDeathRecipient(env, recipient); + status_t err = target->linkToDeath(jdr, recipient, flags); + if (err != NO_ERROR) { + // Failure adding the death recipient, so clear its reference + // now. + jdr->clearReference(); + signalExceptionForError(env, obj, err); + } + } +} + +static jboolean android_os_BinderProxy_unlinkToDeath(JNIEnv* env, jobject obj, + jobject recipient, jint flags) +{ + jboolean res = JNI_FALSE; + if (recipient == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return res; + } + + IBinder* target = (IBinder*) + env->GetIntField(obj, gBinderProxyOffsets.mObject); + if (target == NULL) { + LOGW("Binder has been finalized when calling linkToDeath() with recip=%p)\n", recipient); + return JNI_FALSE; + } + + LOGV("unlinkToDeath: binder=%p recipient=%p\n", target, recipient); + + if (!target->localBinder()) { + wp<IBinder::DeathRecipient> dr; + status_t err = target->unlinkToDeath(NULL, recipient, flags, &dr); + if (err == NO_ERROR && dr != NULL) { + sp<IBinder::DeathRecipient> sdr = dr.promote(); + JavaDeathRecipient* jdr = static_cast<JavaDeathRecipient*>(sdr.get()); + if (jdr != NULL) { + jdr->clearReference(); + } + } + if (err == NO_ERROR || err == DEAD_OBJECT) { + res = JNI_TRUE; + } else { + jniThrowException(env, "java/util/NoSuchElementException", + "Death link does not exist"); + } + } + + return res; +} + +static void android_os_BinderProxy_destroy(JNIEnv* env, jobject obj) +{ + IBinder* b = (IBinder*) + env->GetIntField(obj, gBinderProxyOffsets.mObject); + LOGV("Destroying BinderProxy %p: binder=%p\n", obj, b); + env->SetIntField(obj, gBinderProxyOffsets.mObject, 0); + b->decStrong(obj); + IPCThreadState::self()->flushCommands(); +} + +// ---------------------------------------------------------------------------- + +static const JNINativeMethod gBinderProxyMethods[] = { + /* name, signature, funcPtr */ + {"pingBinder", "()Z", (void*)android_os_BinderProxy_pingBinder}, + {"isBinderAlive", "()Z", (void*)android_os_BinderProxy_isBinderAlive}, + {"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor}, + {"transact", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact}, + {"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath}, + {"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath}, + {"destroy", "()V", (void*)android_os_BinderProxy_destroy}, +}; + +const char* const kBinderProxyPathName = "android/os/BinderProxy"; + +static int int_register_android_os_BinderProxy(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass("java/lang/ref/WeakReference"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.lang.ref.WeakReference"); + gWeakReferenceOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gWeakReferenceOffsets.mGet + = env->GetMethodID(clazz, "get", "()Ljava/lang/Object;"); + assert(gWeakReferenceOffsets.mGet); + + clazz = env->FindClass("java/lang/Error"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.lang.Error"); + gErrorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + + clazz = env->FindClass(kBinderProxyPathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.BinderProxy"); + + gBinderProxyOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gBinderProxyOffsets.mConstructor + = env->GetMethodID(clazz, "<init>", "()V"); + assert(gBinderProxyOffsets.mConstructor); + gBinderProxyOffsets.mSendDeathNotice + = env->GetStaticMethodID(clazz, "sendDeathNotice", "(Landroid/os/IBinder$DeathRecipient;)V"); + assert(gBinderProxyOffsets.mSendDeathNotice); + + gBinderProxyOffsets.mObject + = env->GetFieldID(clazz, "mObject", "I"); + assert(gBinderProxyOffsets.mObject); + gBinderProxyOffsets.mSelf + = env->GetFieldID(clazz, "mSelf", "Ljava/lang/ref/WeakReference;"); + assert(gBinderProxyOffsets.mSelf); + + return AndroidRuntime::registerNativeMethods( + env, kBinderProxyPathName, + gBinderProxyMethods, NELEM(gBinderProxyMethods)); +} + +// **************************************************************************** +// **************************************************************************** +// **************************************************************************** + +static jint android_os_Parcel_dataSize(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + return parcel ? parcel->dataSize() : 0; +} + +static jint android_os_Parcel_dataAvail(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + return parcel ? parcel->dataAvail() : 0; +} + +static jint android_os_Parcel_dataPosition(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + return parcel ? parcel->dataPosition() : 0; +} + +static jint android_os_Parcel_dataCapacity(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + return parcel ? parcel->dataCapacity() : 0; +} + +static void android_os_Parcel_setDataSize(JNIEnv* env, jobject clazz, jint size) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->setDataSize(size); + if (err != NO_ERROR) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + } + } +} + +static void android_os_Parcel_setDataPosition(JNIEnv* env, jobject clazz, jint pos) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + parcel->setDataPosition(pos); + } +} + +static void android_os_Parcel_setDataCapacity(JNIEnv* env, jobject clazz, jint size) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->setDataCapacity(size); + if (err != NO_ERROR) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + } + } +} + +static void android_os_Parcel_writeNative(JNIEnv* env, jobject clazz, + jobject data, jint offset, + jint length) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel == NULL) { + return; + } + void *dest; + + const status_t err = parcel->writeInt32(length); + if (err != NO_ERROR) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + } + + dest = parcel->writeInplace(length); + + if (dest == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return; + } + + jbyte* ar = (jbyte*)env->GetPrimitiveArrayCritical((jarray)data, 0); + if (ar) { + memcpy(dest, ar, length); + env->ReleasePrimitiveArrayCritical((jarray)data, ar, 0); + } +} + + +static void android_os_Parcel_writeInt(JNIEnv* env, jobject clazz, jint val) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->writeInt32(val); + if (err != NO_ERROR) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + } + } +} + +static void android_os_Parcel_writeLong(JNIEnv* env, jobject clazz, jlong val) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->writeInt64(val); + if (err != NO_ERROR) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + } + } +} + +static void android_os_Parcel_writeFloat(JNIEnv* env, jobject clazz, jfloat val) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->writeFloat(val); + if (err != NO_ERROR) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + } + } +} + +static void android_os_Parcel_writeDouble(JNIEnv* env, jobject clazz, jdouble val) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->writeDouble(val); + if (err != NO_ERROR) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + } + } +} + +static void android_os_Parcel_writeString(JNIEnv* env, jobject clazz, jstring val) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + status_t err = NO_MEMORY; + if (val) { + const jchar* str = env->GetStringCritical(val, 0); + if (str) { + err = parcel->writeString16(str, env->GetStringLength(val)); + env->ReleaseStringCritical(val, str); + } + } else { + err = parcel->writeString16(NULL, 0); + } + if (err != NO_ERROR) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + } + } +} + +static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jobject clazz, jobject object) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object)); + if (err != NO_ERROR) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + } + } +} + +static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jobject clazz, jobject object) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->writeDupFileDescriptor( + env->GetIntField(object, gFileDescriptorOffsets.mDescriptor)); + if (err != NO_ERROR) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + } + } +} + +static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jobject clazz) +{ + jbyteArray ret = NULL; + + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + int32_t len = parcel->readInt32(); + + // sanity check the stored length against the true data size + if (len >= 0 && len <= (int32_t)parcel->dataAvail()) { + ret = env->NewByteArray(len); + + if (ret != NULL) { + jbyte* a2 = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0); + if (a2) { + const void* data = parcel->readInplace(len); + memcpy(a2, data, len); + env->ReleasePrimitiveArrayCritical(ret, a2, 0); + } + } + } + } + + return ret; +} + +static jint android_os_Parcel_readInt(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + return parcel->readInt32(); + } + return 0; +} + +static jlong android_os_Parcel_readLong(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + return parcel->readInt64(); + } + return 0; +} + +static jfloat android_os_Parcel_readFloat(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + return parcel->readFloat(); + } + return 0; +} + +static jdouble android_os_Parcel_readDouble(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + return parcel->readDouble(); + } + return 0; +} + +static jstring android_os_Parcel_readString(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + size_t len; + const char16_t* str = parcel->readString16Inplace(&len); + if (str) { + return env->NewString(str, len); + } + return NULL; + } + return NULL; +} + +static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + return javaObjectForIBinder(env, parcel->readStrongBinder()); + } + return NULL; +} + +static jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + int fd = parcel->readFileDescriptor(); + if (fd < 0) return NULL; + fd = dup(fd); + if (fd < 0) return NULL; + jobject object = env->NewObject( + gFileDescriptorOffsets.mClass, gFileDescriptorOffsets.mConstructor); + if (object != NULL) { + //LOGI("Created new FileDescriptor %p with fd %d\n", object, fd); + env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, fd); + } + return object; + } + return NULL; +} + +static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jobject clazz, + jstring name, jint mode) +{ + if (name == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return NULL; + } + const jchar* str = env->GetStringCritical(name, 0); + if (str == NULL) { + // Whatever, whatever. + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return NULL; + } + String8 name8(str, env->GetStringLength(name)); + env->ReleaseStringCritical(name, str); + int flags=0; + switch (mode&0x30000000) { + case 0: + case 0x10000000: + flags = O_RDONLY; + break; + case 0x20000000: + flags = O_WRONLY; + break; + case 0x30000000: + flags = O_RDWR; + break; + } + + if (mode&0x08000000) flags |= O_CREAT; + if (mode&0x04000000) flags |= O_TRUNC; + + int realMode = S_IRWXU|S_IRWXG; + if (mode&0x00000001) realMode |= S_IROTH; + if (mode&0x00000002) realMode |= S_IWOTH; + + int fd = open(name8.string(), flags, realMode); + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", NULL); + return NULL; + } + jobject object = newFileDescriptor(env, fd); + if (object == NULL) { + close(fd); + } + return object; +} + +static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jobject clazz, jobject object) +{ + int fd = env->GetIntField(object, gFileDescriptorOffsets.mDescriptor); + if (fd >= 0) { + env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, -1); + //LOGI("Closing ParcelFileDescriptor %d\n", fd); + close(fd); + } +} + +static void android_os_Parcel_freeBuffer(JNIEnv* env, jobject clazz) +{ + int32_t own = env->GetIntField(clazz, gParcelOffsets.mOwnObject); + if (own) { + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + //LOGI("Parcel.freeBuffer() called for C++ Parcel %p\n", parcel); + parcel->freeData(); + } + } +} + +static void android_os_Parcel_init(JNIEnv* env, jobject clazz, jint parcelInt) +{ + Parcel* parcel = (Parcel*)parcelInt; + int own = 0; + if (!parcel) { + //LOGI("Initializing obj %p: creating new Parcel\n", clazz); + own = 1; + parcel = new Parcel; + } else { + //LOGI("Initializing obj %p: given existing Parcel %p\n", clazz, parcel); + } + if (parcel == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return; + } + //LOGI("Initializing obj %p from C++ Parcel %p, own=%d\n", clazz, parcel, own); + env->SetIntField(clazz, gParcelOffsets.mOwnObject, own); + env->SetIntField(clazz, gParcelOffsets.mObject, (int)parcel); +} + +static void android_os_Parcel_destroy(JNIEnv* env, jobject clazz) +{ + int32_t own = env->GetIntField(clazz, gParcelOffsets.mOwnObject); + if (own) { + Parcel* parcel = parcelForJavaObject(env, clazz); + env->SetIntField(clazz, gParcelOffsets.mObject, 0); + //LOGI("Destroying obj %p: deleting C++ Parcel %p\n", clazz, parcel); + delete parcel; + } else { + env->SetIntField(clazz, gParcelOffsets.mObject, 0); + //LOGI("Destroying obj %p: leaving C++ Parcel %p\n", clazz); + } +} + +static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel == NULL) { + return NULL; + } + + // do not marshall if there are binder objects in the parcel + if (parcel->objectsCount()) + { + jniThrowException(env, "java/lang/RuntimeException", "Tried to marshall a Parcel that contained Binder objects."); + return NULL; + } + + jbyteArray ret = env->NewByteArray(parcel->dataSize()); + + if (ret != NULL) + { + jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0); + if (array != NULL) + { + memcpy(array, parcel->data(), parcel->dataSize()); + env->ReleasePrimitiveArrayCritical(ret, array, 0); + } + } + + return ret; +} + +static void android_os_Parcel_unmarshall(JNIEnv* env, jobject clazz, jbyteArray data, jint offset, jint length) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel == NULL || length < 0) { + return; + } + + jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0); + if (array) + { + parcel->setDataSize(length); + parcel->setDataPosition(0); + + void* raw = parcel->writeInplace(length); + memcpy(raw, (array + offset), length); + + env->ReleasePrimitiveArrayCritical(data, array, 0); + } +} + +static void android_os_Parcel_appendFrom(JNIEnv* env, jobject clazz, jobject parcel, jint offset, jint length) +{ + Parcel* thisParcel = parcelForJavaObject(env, clazz); + if (thisParcel == NULL) { + return; + } + Parcel* otherParcel = parcelForJavaObject(env, parcel); + if (otherParcel == NULL) { + return; + } + + (void) thisParcel->appendFrom(otherParcel, offset, length); +} + +static jboolean android_os_Parcel_hasFileDescriptors(JNIEnv* env, jobject clazz) +{ + jboolean ret = JNI_FALSE; + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + if (parcel->hasFileDescriptors()) { + ret = JNI_TRUE; + } + } + return ret; +} + +static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jobject clazz, jstring name) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + // In the current implementation, the token is just the serialized interface name that + // the caller expects to be invoking + const jchar* str = env->GetStringCritical(name, 0); + if (str != NULL) { + parcel->writeInterfaceToken(String16(str, env->GetStringLength(name))); + env->ReleaseStringCritical(name, str); + } + } +} + +static void android_os_Parcel_enforceInterface(JNIEnv* env, jobject clazz, jstring name) +{ + jboolean ret = JNI_FALSE; + + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const jchar* str = env->GetStringCritical(name, 0); + if (str) { + bool isValid = parcel->enforceInterface(String16(str, env->GetStringLength(name))); + env->ReleaseStringCritical(name, str); + if (isValid) { + return; // everything was correct -> return silently + } + } + } + + // all error conditions wind up here + jniThrowException(env, "java/lang/SecurityException", + "Binder invocation to an incorrect interface"); +} + +// ---------------------------------------------------------------------------- + +static const JNINativeMethod gParcelMethods[] = { + {"dataSize", "()I", (void*)android_os_Parcel_dataSize}, + {"dataAvail", "()I", (void*)android_os_Parcel_dataAvail}, + {"dataPosition", "()I", (void*)android_os_Parcel_dataPosition}, + {"dataCapacity", "()I", (void*)android_os_Parcel_dataCapacity}, + {"setDataSize", "(I)V", (void*)android_os_Parcel_setDataSize}, + {"setDataPosition", "(I)V", (void*)android_os_Parcel_setDataPosition}, + {"setDataCapacity", "(I)V", (void*)android_os_Parcel_setDataCapacity}, + {"writeNative", "([BII)V", (void*)android_os_Parcel_writeNative}, + {"writeInt", "(I)V", (void*)android_os_Parcel_writeInt}, + {"writeLong", "(J)V", (void*)android_os_Parcel_writeLong}, + {"writeFloat", "(F)V", (void*)android_os_Parcel_writeFloat}, + {"writeDouble", "(D)V", (void*)android_os_Parcel_writeDouble}, + {"writeString", "(Ljava/lang/String;)V", (void*)android_os_Parcel_writeString}, + {"writeStrongBinder", "(Landroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder}, + {"writeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor}, + {"createByteArray", "()[B", (void*)android_os_Parcel_createByteArray}, + {"readInt", "()I", (void*)android_os_Parcel_readInt}, + {"readLong", "()J", (void*)android_os_Parcel_readLong}, + {"readFloat", "()F", (void*)android_os_Parcel_readFloat}, + {"readDouble", "()D", (void*)android_os_Parcel_readDouble}, + {"readString", "()Ljava/lang/String;", (void*)android_os_Parcel_readString}, + {"readStrongBinder", "()Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder}, + {"internalReadFileDescriptor", "()Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor}, + {"openFileDescriptor", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_openFileDescriptor}, + {"closeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_closeFileDescriptor}, + {"freeBuffer", "()V", (void*)android_os_Parcel_freeBuffer}, + {"init", "(I)V", (void*)android_os_Parcel_init}, + {"destroy", "()V", (void*)android_os_Parcel_destroy}, + {"marshall", "()[B", (void*)android_os_Parcel_marshall}, + {"unmarshall", "([BII)V", (void*)android_os_Parcel_unmarshall}, + {"appendFrom", "(Landroid/os/Parcel;II)V", (void*)android_os_Parcel_appendFrom}, + {"hasFileDescriptors", "()Z", (void*)android_os_Parcel_hasFileDescriptors}, + {"writeInterfaceToken", "(Ljava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken}, + {"enforceInterface", "(Ljava/lang/String;)V", (void*)android_os_Parcel_enforceInterface}, +}; + +const char* const kParcelPathName = "android/os/Parcel"; + +static int int_register_android_os_Parcel(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass("android/util/Log"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.util.Log"); + gLogOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gLogOffsets.mLogE = env->GetStaticMethodID( + clazz, "e", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I"); + assert(gLogOffsets.mLogE); + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gFileDescriptorOffsets.mConstructor + = env->GetMethodID(clazz, "<init>", "()V"); + gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + clazz = env->FindClass("android/os/ParcelFileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); + gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gParcelFileDescriptorOffsets.mConstructor + = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); + + clazz = env->FindClass(kParcelPathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Parcel"); + + gParcelOffsets.mObject + = env->GetFieldID(clazz, "mObject", "I"); + gParcelOffsets.mOwnObject + = env->GetFieldID(clazz, "mOwnObject", "I"); + + return AndroidRuntime::registerNativeMethods( + env, kParcelPathName, + gParcelMethods, NELEM(gParcelMethods)); +} + +int register_android_os_Binder(JNIEnv* env) +{ + if (int_register_android_os_Binder(env) < 0) + return -1; + if (int_register_android_os_BinderInternal(env) < 0) + return -1; + if (int_register_android_os_BinderProxy(env) < 0) + return -1; + if (int_register_android_os_Parcel(env) < 0) + return -1; + return 0; +} + +namespace android { + +// Returns the Unix file descriptor for a ParcelFileDescriptor object +int getParcelFileDescriptorFD(JNIEnv* env, jobject object) +{ + return env->GetIntField(object, gFileDescriptorOffsets.mDescriptor); +} + +} diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h new file mode 100644 index 0000000..16d993d --- /dev/null +++ b/core/jni/android_util_Binder.h @@ -0,0 +1,35 @@ +/* //device/libs/android_runtime/android_util_Binder.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <utils/IBinder.h> + +#include "jni.h" + +namespace android { + +// Converstion to/from Java IBinder Object and C++ IBinder instance. +extern jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val); +extern sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj); + +// Conversion from Java Parcel Object to C++ Parcel instance. +// Note: does not type checking; must guarantee jobject is a Java Parcel +extern Parcel* parcelForJavaObject(JNIEnv* env, jobject obj); + +extern jobject newFileDescriptor(JNIEnv* env, int fd); +extern jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc); + +} diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp new file mode 100644 index 0000000..d0cac18 --- /dev/null +++ b/core/jni/android_util_EventLog.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fcntl.h> + +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" +#include "jni.h" +#include "utils/logger.h" + +#define END_DELIMITER '\n' +#define INT_BUFFER_SIZE (sizeof(jbyte)+sizeof(jint)+sizeof(END_DELIMITER)) +#define LONG_BUFFER_SIZE (sizeof(jbyte)+sizeof(jlong)+sizeof(END_DELIMITER)) +#define INITAL_BUFFER_CAPACITY 256 + +#define MAX(a,b) ((a>b)?a:b) + +namespace android { + +static jclass gCollectionClass; +static jmethodID gCollectionAddID; + +static jclass gEventClass; +static jmethodID gEventInitID; + +static jclass gIntegerClass; +static jfieldID gIntegerValueID; + +static jclass gListClass; +static jfieldID gListItemsID; + +static jclass gLongClass; +static jfieldID gLongValueID; + +static jclass gStringClass; + +struct ByteBuf { + size_t len; + size_t capacity; + uint8_t* buf; + + ByteBuf(size_t initSize) { + buf = (uint8_t*)malloc(initSize); + len = 0; + capacity = initSize; + } + + ~ByteBuf() { + free(buf); + } + + bool ensureExtraCapacity(size_t extra) { + size_t spaceNeeded = len + extra; + if (spaceNeeded > capacity) { + size_t newCapacity = MAX(spaceNeeded, 2 * capacity); + void* newBuf = realloc(buf, newCapacity); + if (newBuf == NULL) { + return false; + } + capacity = newCapacity; + buf = (uint8_t*)newBuf; + return true; + } else { + return true; + } + } + + void putIntEvent(jint value) { + bool succeeded = ensureExtraCapacity(INT_BUFFER_SIZE); + buf[len++] = EVENT_TYPE_INT; + memcpy(buf+len, &value, sizeof(jint)); + len += sizeof(jint); + } + + void putByte(uint8_t value) { + bool succeeded = ensureExtraCapacity(sizeof(uint8_t)); + buf[len++] = value; + } + + void putLongEvent(jlong value) { + bool succeeded = ensureExtraCapacity(LONG_BUFFER_SIZE); + buf[len++] = EVENT_TYPE_LONG; + memcpy(buf+len, &value, sizeof(jlong)); + len += sizeof(jlong); + } + + + void putStringEvent(JNIEnv* env, jstring value) { + const char* strValue = env->GetStringUTFChars(value, NULL); + uint32_t strLen = strlen(strValue); //env->GetStringUTFLength(value); + bool succeeded = ensureExtraCapacity(1 + sizeof(uint32_t) + strLen); + buf[len++] = EVENT_TYPE_STRING; + memcpy(buf+len, &strLen, sizeof(uint32_t)); + len += sizeof(uint32_t); + memcpy(buf+len, strValue, strLen); + env->ReleaseStringUTFChars(value, strValue); + len += strLen; + } + + void putList(JNIEnv* env, jobject list) { + jobjectArray items = (jobjectArray) env->GetObjectField(list, gListItemsID); + if (items == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + jsize numItems = env->GetArrayLength(items); + putByte(EVENT_TYPE_LIST); + putByte(numItems); + // We'd like to call GetPrimitveArrayCritical() but that might + // not be safe since we're going to be doing some I/O + for (int i = 0; i < numItems; i++) { + jobject item = env->GetObjectArrayElement(items, i); + if (env->IsInstanceOf(item, gIntegerClass)) { + jint intVal = env->GetIntField(item, gIntegerValueID); + putIntEvent(intVal); + } else if (env->IsInstanceOf(item, gLongClass)) { + jlong longVal = env->GetLongField(item, gLongValueID); + putLongEvent(longVal); + } else if (env->IsInstanceOf(item, gStringClass)) { + putStringEvent(env, (jstring)item); + } else if (env->IsInstanceOf(item, gListClass)) { + putList(env, item); + } else { + jniThrowException( + env, + "java/lang/IllegalArgumentException", + "Attempt to log an illegal item type."); + return; + } + env->DeleteLocalRef(item); + } + + env->DeleteLocalRef(items); + } +}; + +/* + * In class android.util.EventLog: + * static native int writeEvent(int tag, int value) + */ +static jint android_util_EventLog_writeEvent_Integer(JNIEnv* env, jobject clazz, + jint tag, jint value) +{ + return android_btWriteLog(tag, EVENT_TYPE_INT, &value, sizeof(value)); +} + +/* + * In class android.util.EventLog: + * static native int writeEvent(long tag, long value) + */ +static jint android_util_EventLog_writeEvent_Long(JNIEnv* env, jobject clazz, + jint tag, jlong value) +{ + return android_btWriteLog(tag, EVENT_TYPE_LONG, &value, sizeof(value)); +} + +/* + * In class android.util.EventLog: + * static native int writeEvent(long tag, List value) + */ +static jint android_util_EventLog_writeEvent_List(JNIEnv* env, jobject clazz, + jint tag, jobject value) { + if (value == NULL) { + jclass clazz = env->FindClass("java/lang/IllegalArgumentException"); + env->ThrowNew(clazz, "writeEvent needs a value."); + return -1; + } + ByteBuf byteBuf(INITAL_BUFFER_CAPACITY); + byteBuf.putList(env, value); + byteBuf.putByte((uint8_t)END_DELIMITER); + int numBytesPut = byteBuf.len; + int bytesWritten = android_bWriteLog(tag, byteBuf.buf, numBytesPut); + return bytesWritten; +} + +/* + * In class android.util.EventLog: + * static native int writeEvent(int tag, String value) + */ +static jint android_util_EventLog_writeEvent_String(JNIEnv* env, jobject clazz, + jint tag, jstring value) { + if (value == NULL) { + jclass clazz = env->FindClass("java/lang/IllegalArgumentException"); + env->ThrowNew(clazz, "logEvent needs a value."); + return -1; + } + + ByteBuf byteBuf(INITAL_BUFFER_CAPACITY); + byteBuf.putStringEvent(env, value); + byteBuf.putByte((uint8_t)END_DELIMITER); + int numBytesPut = byteBuf.len; + int bytesWritten = android_bWriteLog(tag, byteBuf.buf, numBytesPut); + return bytesWritten; +} + +/* + * In class android.util.EventLog: + * static native void readEvents(int[] tags, Collection<Event> output) + */ +static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz, + jintArray tags, + jobject out) { + if (tags == NULL || out == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + int fd = open("/dev/" LOGGER_LOG_EVENTS, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + jniThrowIOException(env, errno); + return; + } + + jsize tagLength = env->GetArrayLength(tags); + jint *tagValues = env->GetIntArrayElements(tags, NULL); + + uint8_t buf[LOGGER_ENTRY_MAX_LEN]; + for (;;) { + int len = read(fd, buf, sizeof(buf)); + if (len == 0 || (len < 0 && errno == EAGAIN)) { + break; + } else if (len < 0) { + // This calls env->ThrowNew(), which doesn't throw an exception + // now, but sets a flag to trigger an exception after we return. + jniThrowIOException(env, errno); + break; + } else if ((size_t) len < sizeof(logger_entry) + sizeof(int32_t)) { + jniThrowException(env, "java/io/IOException", "Event too short"); + break; + } + + logger_entry* entry = (logger_entry*) buf; + int32_t tag = * (int32_t*) (buf + sizeof(*entry)); + + int found = 0; + for (int i = 0; !found && i < tagLength; ++i) { + found = (tag == tagValues[i]); + } + + if (found) { + jsize len = sizeof(*entry) + entry->len; + jbyteArray array = env->NewByteArray(len); + if (array == NULL) break; + + jbyte *bytes = env->GetByteArrayElements(array, NULL); + memcpy(bytes, buf, len); + env->ReleaseByteArrayElements(array, bytes, 0); + + jobject event = env->NewObject(gEventClass, gEventInitID, array); + if (event == NULL) break; + + env->CallBooleanMethod(out, gCollectionAddID, event); + env->DeleteLocalRef(event); + env->DeleteLocalRef(array); + } + } + + close(fd); + env->ReleaseIntArrayElements(tags, tagValues, 0); +} + + +/* + * JNI registration. + */ +static JNINativeMethod gRegisterMethods[] = { + /* name, signature, funcPtr */ + { "writeEvent", "(II)I", (void*) android_util_EventLog_writeEvent_Integer }, + { "writeEvent", "(IJ)I", (void*) android_util_EventLog_writeEvent_Long }, + { "writeEvent", + "(ILjava/lang/String;)I", + (void*) android_util_EventLog_writeEvent_String + }, + { "writeEvent", + "(ILandroid/util/EventLog$List;)I", + (void*) android_util_EventLog_writeEvent_List + }, + { "readEvents", + "([ILjava/util/Collection;)V", + (void*) android_util_EventLog_readEvents + } +}; + +static struct { const char *name; jclass *clazz; } gClasses[] = { + { "android/util/EventLog$Event", &gEventClass }, + { "android/util/EventLog$List", &gListClass }, + { "java/lang/Integer", &gIntegerClass }, + { "java/lang/Long", &gLongClass }, + { "java/lang/String", &gStringClass }, + { "java/util/Collection", &gCollectionClass }, +}; + +static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = { + { &gIntegerClass, "value", "I", &gIntegerValueID }, + { &gListClass, "mItems", "[Ljava/lang/Object;", &gListItemsID }, + { &gLongClass, "value", "J", &gLongValueID }, +}; + +static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = { + { &gEventClass, "<init>", "([B)V", &gEventInitID }, + { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID }, +}; + +int register_android_util_EventLog(JNIEnv* env) { + for (int i = 0; i < NELEM(gClasses); ++i) { + jclass clazz = env->FindClass(gClasses[i].name); + if (clazz == NULL) { + LOGE("Can't find class: %s\n", gClasses[i].name); + return -1; + } + *gClasses[i].clazz = (jclass) env->NewGlobalRef(clazz); + } + + for (int i = 0; i < NELEM(gFields); ++i) { + *gFields[i].id = env->GetFieldID( + *gFields[i].c, gFields[i].name, gFields[i].ft); + if (*gFields[i].id == NULL) { + LOGE("Can't find field: %s\n", gFields[i].name); + return -1; + } + } + + for (int i = 0; i < NELEM(gMethods); ++i) { + *gMethods[i].id = env->GetMethodID( + *gMethods[i].c, gMethods[i].name, gMethods[i].mt); + if (*gMethods[i].id == NULL) { + LOGE("Can't find method: %s\n", gMethods[i].name); + return -1; + } + } + + return AndroidRuntime::registerNativeMethods( + env, + "android/util/EventLog", + gRegisterMethods, NELEM(gRegisterMethods)); +} + +}; // namespace android + diff --git a/core/jni/android_util_FileObserver.cpp b/core/jni/android_util_FileObserver.cpp new file mode 100644 index 0000000..794478a --- /dev/null +++ b/core/jni/android_util_FileObserver.cpp @@ -0,0 +1,157 @@ +/* //device/libs/android_runtime/android_util_FileObserver.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" +#include "android_runtime/AndroidRuntime.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> + +#ifdef HAVE_INOTIFY +#include <sys/inotify.h> +#endif + +namespace android { + +static jmethodID method_onEvent; + +static jint android_os_fileobserver_init(JNIEnv* env, jobject object) +{ +#ifdef HAVE_INOTIFY + + return (jint)inotify_init(); + +#else // HAVE_INOTIFY + + return -1; + +#endif // HAVE_INOTIFY +} + +static void android_os_fileobserver_observe(JNIEnv* env, jobject object, jint fd) +{ +#ifdef HAVE_INOTIFY + + char event_buf[512]; + struct inotify_event* event; + + while (1) + { + int event_pos = 0; + int num_bytes = read(fd, event_buf, sizeof(event_buf)); + + if (num_bytes < (int)sizeof(*event)) + { + if (errno == EINTR) + continue; + + LOGE("***** ERROR! android_os_fileobserver_observe() got a short event!"); + return; + } + + while (num_bytes >= (int)sizeof(*event)) + { + int event_size; + event = (struct inotify_event *)(event_buf + event_pos); + + jstring path = NULL; + + if (event->len > 0) + { + path = env->NewStringUTF(event->name); + } + + env->CallVoidMethod(object, method_onEvent, event->wd, event->mask, path); + + event_size = sizeof(*event) + event->len; + num_bytes -= event_size; + event_pos += event_size; + } + } + +#endif // HAVE_INOTIFY +} + +static jint android_os_fileobserver_startWatching(JNIEnv* env, jobject object, jint fd, jstring pathString, jint mask) +{ + int res = -1; + +#ifdef HAVE_INOTIFY + + if (fd >= 0) + { + const char* path = env->GetStringUTFChars(pathString, NULL); + + res = inotify_add_watch(fd, path, mask); + + env->ReleaseStringUTFChars(pathString, path); + } + +#endif // HAVE_INOTIFY + + return res; +} + +static void android_os_fileobserver_stopWatching(JNIEnv* env, jobject object, jint fd, jint wfd) +{ +#ifdef HAVE_INOTIFY + + inotify_rm_watch((int)fd, (uint32_t)wfd); + +#endif // HAVE_INOTIFY +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + { "init", "()I", (void*)android_os_fileobserver_init }, + { "observe", "(I)V", (void*)android_os_fileobserver_observe }, + { "startWatching", "(ILjava/lang/String;I)I", (void*)android_os_fileobserver_startWatching }, + { "stopWatching", "(II)V", (void*)android_os_fileobserver_stopWatching } + +}; + +int register_android_os_FileObserver(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass("android/os/FileObserver$ObserverThread"); + + if (clazz == NULL) + { + LOGE("Can't find android/os/FileObserver$ObserverThread"); + return -1; + } + + method_onEvent = env->GetMethodID(clazz, "onEvent", "(IILjava/lang/String;)V"); + if (method_onEvent == NULL) + { + LOGE("Can't find FileObserver.onEvent(int, int, String)"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, "android/os/FileObserver$ObserverThread", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/android_util_FloatMath.cpp b/core/jni/android_util_FloatMath.cpp new file mode 100644 index 0000000..f38faa9 --- /dev/null +++ b/core/jni/android_util_FloatMath.cpp @@ -0,0 +1,46 @@ +#include "jni.h" +#include <android_runtime/AndroidRuntime.h> +#include <math.h> +#include <float.h> +#include "SkTypes.h" + +class MathUtilsGlue { +public: + static float FloorF(JNIEnv* env, jobject clazz, float x) { + return floorf(x); + } + + static float CeilF(JNIEnv* env, jobject clazz, float x) { + return ceilf(x); + } + + static float SinF(JNIEnv* env, jobject clazz, float x) { + return sinf(x); + } + + static float CosF(JNIEnv* env, jobject clazz, float x) { + return cosf(x); + } + + static float SqrtF(JNIEnv* env, jobject clazz, float x) { + return sqrtf(x); + } +}; + +static JNINativeMethod gMathUtilsMethods[] = { + {"floor", "(F)F", (void*) MathUtilsGlue::FloorF}, + {"ceil", "(F)F", (void*) MathUtilsGlue::CeilF}, + {"sin", "(F)F", (void*) MathUtilsGlue::SinF}, + {"cos", "(F)F", (void*) MathUtilsGlue::CosF}, + {"sqrt", "(F)F", (void*) MathUtilsGlue::SqrtF} +}; + +int register_android_util_FloatMath(JNIEnv* env) +{ + int result = android::AndroidRuntime::registerNativeMethods(env, + "android/util/FloatMath", + gMathUtilsMethods, + SK_ARRAY_COUNT(gMathUtilsMethods)); + return result; +} + diff --git a/core/jni/android_util_Log.cpp b/core/jni/android_util_Log.cpp new file mode 100644 index 0000000..8316b03 --- /dev/null +++ b/core/jni/android_util_Log.cpp @@ -0,0 +1,161 @@ +/* //device/libs/android_runtime/android_util_Log.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_NAMESPACE "log.tag." +#define LOG_TAG "Log_println" + +#include <assert.h> +#include <cutils/properties.h> +#include <utils/Log.h> +#include <utils/String8.h> + +#include "jni.h" +#include "utils/misc.h" +#include "android_runtime/AndroidRuntime.h" + +#define MIN(a,b) ((a<b)?a:b) + +namespace android { + +struct levels_t { + jint verbose; + jint debug; + jint info; + jint warn; + jint error; + jint assert; +}; +static levels_t levels; + +static int toLevel(const char* value) +{ + switch (value[0]) { + case 'V': return levels.verbose; + case 'D': return levels.debug; + case 'I': return levels.info; + case 'W': return levels.warn; + case 'E': return levels.error; + case 'A': return levels.assert; + case 'S': return -1; // SUPPRESS + } + return levels.info; +} + +static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level) +{ +#ifndef HAVE_ANDROID_OS + return false; +#else /* HAVE_ANDROID_OS */ + int len; + char key[PROPERTY_KEY_MAX]; + char buf[PROPERTY_VALUE_MAX]; + + if (tag == NULL) { + return false; + } + + jboolean result = false; + + const char* chars = env->GetStringUTFChars(tag, NULL); + + if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) { + jclass clazz = env->FindClass("java/lang/IllegalArgumentException"); + char buf2[200]; + snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters\n", + chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE)); + + // release the chars! + env->ReleaseStringUTFChars(tag, chars); + + env->ThrowNew(clazz, buf2); + return false; + } else { + strncpy(key, LOG_NAMESPACE, sizeof(LOG_NAMESPACE)-1); + strcpy(key + sizeof(LOG_NAMESPACE) - 1, chars); + } + + env->ReleaseStringUTFChars(tag, chars); + + len = property_get(key, buf, ""); + int logLevel = toLevel(buf); + return (logLevel >= 0 && level >= logLevel) ? true : false; +#endif /* HAVE_ANDROID_OS */ +} + +/* + * In class android.util.Log: + * public static native int println(int priority, String tag, String msg) + */ +static jint android_util_Log_println(JNIEnv* env, jobject clazz, + jint priority, jstring tagObj, jstring msgObj) +{ + const char* tag = NULL; + const char* msg = NULL; + + if (msgObj == NULL) { + jclass npeClazz; + + npeClazz = env->FindClass("java/lang/NullPointerException"); + assert(npeClazz != NULL); + + env->ThrowNew(npeClazz, "println needs a message"); + return -1; + } + + if (tagObj != NULL) + tag = env->GetStringUTFChars(tagObj, NULL); + msg = env->GetStringUTFChars(msgObj, NULL); + + int res = android_writeLog((android_LogPriority) priority, tag, msg); + + if (tag != NULL) + env->ReleaseStringUTFChars(tagObj, tag); + env->ReleaseStringUTFChars(msgObj, msg); + + return res; +} + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable }, + { "println", "(ILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println }, +}; + +int register_android_util_Log(JNIEnv* env) +{ + jclass clazz = env->FindClass("android/util/Log"); + + if (clazz == NULL) { + LOGE("Can't find android/util/Log"); + return -1; + } + + levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I")); + levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I")); + levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I")); + levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I")); + levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I")); + levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I")); + + return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods)); +} + +}; // namespace android + diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp new file mode 100644 index 0000000..08c4f1c --- /dev/null +++ b/core/jni/android_util_Process.cpp @@ -0,0 +1,743 @@ +/* //device/libs/android_runtime/android_util_Process.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "Process" + +#include <utils/Log.h> +#include <utils/IPCThreadState.h> +#include <utils/ProcessState.h> +#include <utils/IServiceManager.h> +#include <utils/String8.h> +#include <utils/Vector.h> + +#include <android_runtime/AndroidRuntime.h> + +#include "android_util_Binder.h" +#include "JNIHelp.h" + +#include <sys/errno.h> +#include <sys/resource.h> +#include <sys/types.h> +#include <dirent.h> +#include <fcntl.h> +#include <grp.h> +#include <pwd.h> +#include <signal.h> + +/* desktop Linux needs a little help with gettid() */ +#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS) +#define __KERNEL__ +# include <linux/unistd.h> +#ifdef _syscall0 +_syscall0(pid_t,gettid) +#else +pid_t gettid() { return syscall(__NR_gettid);} +#endif +#undef __KERNEL__ +#endif + +using namespace android; + +static void signalExceptionForPriorityError(JNIEnv* env, jobject obj, int err) +{ + switch (err) { + case EINVAL: + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + break; + case ESRCH: + jniThrowException(env, "java/lang/IllegalArgumentException", "Given thread does not exist"); + break; + case EPERM: + jniThrowException(env, "java/lang/SecurityException", "No permission to modify given thread"); + break; + case EACCES: + jniThrowException(env, "java/lang/SecurityException", "No permission to set to given priority"); + break; + default: + jniThrowException(env, "java/lang/RuntimeException", "Unknown error"); + break; + } +} + +static void fakeProcessEntry(void* arg) +{ + String8* cls = (String8*)arg; + + AndroidRuntime* jr = AndroidRuntime::getRuntime(); + jr->callMain(cls->string(), 0, NULL); + + delete cls; +} + +jint android_os_Process_myPid(JNIEnv* env, jobject clazz) +{ + return getpid(); +} + +jint android_os_Process_myTid(JNIEnv* env, jobject clazz) +{ +#ifdef HAVE_GETTID + return gettid(); +#else + return getpid(); +#endif +} + +jint android_os_Process_getUidForName(JNIEnv* env, jobject clazz, jstring name) +{ + if (name == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return -1; + } + + const jchar* str16 = env->GetStringCritical(name, 0); + String8 name8; + if (str16) { + name8 = String8(str16, env->GetStringLength(name)); + env->ReleaseStringCritical(name, str16); + } + + const size_t N = name8.size(); + if (N > 0) { + const char* str = name8.string(); + for (size_t i=0; i<N; i++) { + if (str[i] < '0' || str[i] > '9') { + struct passwd* pwd = getpwnam(str); + if (pwd == NULL) { + return -1; + } + return pwd->pw_uid; + } + } + return atoi(str); + } + return -1; +} + +jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name) +{ + if (name == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return -1; + } + + const jchar* str16 = env->GetStringCritical(name, 0); + String8 name8; + if (str16) { + name8 = String8(str16, env->GetStringLength(name)); + env->ReleaseStringCritical(name, str16); + } + + const size_t N = name8.size(); + if (N > 0) { + const char* str = name8.string(); + for (size_t i=0; i<N; i++) { + if (str[i] < '0' || str[i] > '9') { + struct group* grp = getgrnam(str); + if (grp == NULL) { + return -1; + } + return grp->gr_gid; + } + } + return atoi(str); + } + return -1; +} + +void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz, + jint pid, jint pri) +{ + if (setpriority(PRIO_PROCESS, pid, pri) < 0) { + signalExceptionForPriorityError(env, clazz, errno); + } + //LOGI("Setting priority of %d: %d, getpriority returns %d\n", + // pid, pri, getpriority(PRIO_PROCESS, pid)); +} + +void android_os_Process_setCallingThreadPriority(JNIEnv* env, jobject clazz, + jint pri) +{ + jint tid = android_os_Process_myTid(env, clazz); + android_os_Process_setThreadPriority(env, clazz, tid, pri); +} + +jint android_os_Process_getThreadPriority(JNIEnv* env, jobject clazz, + jint pid) +{ + errno = 0; + jint pri = getpriority(PRIO_PROCESS, pid); + if (errno != 0) { + signalExceptionForPriorityError(env, clazz, errno); + } + //LOGI("Returning priority of %d: %d\n", pid, pri); + return pri; +} + +jboolean android_os_Process_setOomAdj(JNIEnv* env, jobject clazz, + jint pid, jint adj) +{ +#ifdef HAVE_OOM_ADJ + if (ProcessState::self()->supportsProcesses()) { + char text[64]; + sprintf(text, "/proc/%d/oom_adj", pid); + int fd = open(text, O_WRONLY); + if (fd >= 0) { + sprintf(text, "%d", adj); + write(fd, text, strlen(text)); + close(fd); + return true; + } + } +#endif + return false; +} + +void android_os_Process_setArgV0(JNIEnv* env, jobject clazz, jstring name) +{ + if (name == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const jchar* str = env->GetStringCritical(name, 0); + String8 name8; + if (str) { + name8 = String8(str, env->GetStringLength(name)); + env->ReleaseStringCritical(name, str); + } + + if (name8.size() > 0) { + ProcessState::self()->setArgV0(name8.string()); + } +} + +jint android_os_Process_setUid(JNIEnv* env, jobject clazz, jint uid) +{ + #if HAVE_ANDROID_OS + return setuid(uid) == 0 ? 0 : errno; + #else + return ENOSYS; + #endif +} + +jint android_os_Process_setGid(JNIEnv* env, jobject clazz, jint uid) +{ + #if HAVE_ANDROID_OS + return setgid(uid) == 0 ? 0 : errno; + #else + return ENOSYS; + #endif +} + +jboolean android_os_Process_supportsProcesses(JNIEnv* env, jobject clazz) +{ + return ProcessState::self()->supportsProcesses(); +} + +static int pid_compare(const void* v1, const void* v2) +{ + //LOGI("Compare %d vs %d\n", *((const jint*)v1), *((const jint*)v2)); + return *((const jint*)v1) - *((const jint*)v2); +} + +jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz) +{ + int fd = open("/proc/meminfo", O_RDONLY); + + if (fd < 0) { + LOGW("Unable to open /proc/meminfo"); + return -1; + } + + char buffer[256]; + const int len = read(fd, buffer, sizeof(buffer)-1); + close(fd); + + if (len < 0) { + LOGW("Unable to read /proc/meminfo"); + return -1; + } + buffer[len] = 0; + + int numFound = 0; + int mem = 0; + + static const char* const sums[] = { "MemFree:", "Cached:", NULL }; + static const int sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), NULL }; + + char* p = buffer; + while (*p && numFound < 2) { + int i = 0; + while (sums[i]) { + if (strncmp(p, sums[i], sumsLen[i]) == 0) { + p += sumsLen[i]; + while (*p == ' ') p++; + char* num = p; + while (*p >= '0' && *p <= '9') p++; + if (*p != 0) { + *p = 0; + p++; + if (*p == 0) p--; + } + mem += atoi(num) * 1024; + numFound++; + break; + } + i++; + } + p++; + } + + return numFound > 0 ? mem : -1; +} + +void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileStr, + jobjectArray reqFields, jlongArray outFields) +{ + //LOGI("getMemInfo: %p %p", reqFields, outFields); + + if (fileStr == NULL || reqFields == NULL || outFields == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const char* file8 = env->GetStringUTFChars(fileStr, NULL); + if (file8 == NULL) { + return; + } + String8 file(file8); + env->ReleaseStringUTFChars(fileStr, file8); + + jsize count = env->GetArrayLength(reqFields); + if (count > env->GetArrayLength(outFields)) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Array lengths differ"); + return; + } + + Vector<String8> fields; + int i; + + for (i=0; i<count; i++) { + jobject obj = env->GetObjectArrayElement(reqFields, i); + if (obj != NULL) { + const char* str8 = env->GetStringUTFChars((jstring)obj, NULL); + //LOGI("String at %d: %p = %s", i, obj, str8); + if (str8 == NULL) { + jniThrowException(env, "java/lang/NullPointerException", "Element in reqFields"); + return; + } + fields.add(String8(str8)); + env->ReleaseStringUTFChars((jstring)obj, str8); + } else { + jniThrowException(env, "java/lang/NullPointerException", "Element in reqFields"); + return; + } + } + + jlong* sizesArray = env->GetLongArrayElements(outFields, 0); + if (sizesArray == NULL) { + return; + } + + //LOGI("Clearing %d sizes", count); + for (i=0; i<count; i++) { + sizesArray[i] = 0; + } + + int fd = open(file.string(), O_RDONLY); + + if (fd >= 0) { + const size_t BUFFER_SIZE = 2048; + char* buffer = (char*)malloc(BUFFER_SIZE); + int len = read(fd, buffer, BUFFER_SIZE-1); + close(fd); + + if (len < 0) { + LOGW("Unable to read %s", file.string()); + len = 0; + } + buffer[len] = 0; + + int foundCount = 0; + + char* p = buffer; + while (*p && foundCount < count) { + bool skipToEol = true; + //LOGI("Parsing at: %s", p); + for (i=0; i<count; i++) { + const String8& field = fields[i]; + if (strncmp(p, field.string(), field.length()) == 0) { + p += field.length(); + while (*p == ' ') p++; + char* num = p; + while (*p >= '0' && *p <= '9') p++; + skipToEol = *p != '\n'; + if (*p != 0) { + *p = 0; + p++; + } + char* end; + sizesArray[i] = strtoll(num, &end, 10); + //LOGI("Field %s = %d", field.string(), sizesArray[i]); + foundCount++; + break; + } + } + if (skipToEol) { + while (*p && *p != '\n') { + p++; + } + if (*p == '\n') { + p++; + } + } + } + + free(buffer); + } else { + LOGW("Unable to open %s", file.string()); + } + + //LOGI("Done!"); + env->ReleaseLongArrayElements(outFields, sizesArray, 0); +} + +jintArray android_os_Process_getPids(JNIEnv* env, jobject clazz, + jstring file, jintArray lastArray) +{ + if (file == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return NULL; + } + + const char* file8 = env->GetStringUTFChars(file, NULL); + if (file8 == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return NULL; + } + + DIR* dirp = opendir(file8); + + env->ReleaseStringUTFChars(file, file8); + + if(dirp == NULL) { + return NULL; + } + + jsize curCount = 0; + jint* curData = NULL; + if (lastArray != NULL) { + curCount = env->GetArrayLength(lastArray); + curData = env->GetIntArrayElements(lastArray, 0); + } + + jint curPos = 0; + + struct dirent* entry; + while ((entry=readdir(dirp)) != NULL) { + const char* p = entry->d_name; + while (*p) { + if (*p < '0' || *p > '9') break; + p++; + } + if (*p != 0) continue; + + char* end; + int pid = strtol(entry->d_name, &end, 10); + //LOGI("File %s pid=%d\n", entry->d_name, pid); + if (curPos >= curCount) { + jsize newCount = (curCount == 0) ? 10 : (curCount*2); + jintArray newArray = env->NewIntArray(newCount); + if (newArray == NULL) { + closedir(dirp); + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return NULL; + } + jint* newData = env->GetIntArrayElements(newArray, 0); + if (curData != NULL) { + memcpy(newData, curData, sizeof(jint)*curCount); + env->ReleaseIntArrayElements(lastArray, curData, 0); + } + lastArray = newArray; + curCount = newCount; + curData = newData; + } + + curData[curPos] = pid; + curPos++; + } + + closedir(dirp); + + if (curData != NULL && curPos > 0) { + qsort(curData, curPos, sizeof(jint), pid_compare); + } + + while (curPos < curCount) { + curData[curPos] = -1; + curPos++; + } + + if (curData != NULL) { + env->ReleaseIntArrayElements(lastArray, curData, 0); + } + + return lastArray; +} + +enum { + PROC_TERM_MASK = 0xff, + PROC_ZERO_TERM = 0, + PROC_SPACE_TERM = ' ', + PROC_COMBINE = 0x100, + PROC_PARENS = 0x200, + PROC_OUT_STRING = 0x1000, + PROC_OUT_LONG = 0x2000, + PROC_OUT_FLOAT = 0x4000, +}; + +jboolean android_os_Process_readProcFile(JNIEnv* env, jobject clazz, + jstring file, jintArray format, jobjectArray outStrings, + jlongArray outLongs, jfloatArray outFloats) +{ + if (file == NULL || format == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return JNI_FALSE; + } + + const char* file8 = env->GetStringUTFChars(file, NULL); + if (file8 == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return JNI_FALSE; + } + int fd = open(file8, O_RDONLY); + env->ReleaseStringUTFChars(file, file8); + + if (fd < 0) { + //LOGW("Unable to open process file: %s\n", file8); + return JNI_FALSE; + } + + char buffer[256]; + const int len = read(fd, buffer, sizeof(buffer)-1); + close(fd); + + if (len < 0) { + //LOGW("Unable to open process file: %s fd=%d\n", file8, fd); + return JNI_FALSE; + } + buffer[len] = 0; + + //LOGI("Process file %s: %s\n", file8, buffer); + + const jsize NF = env->GetArrayLength(format); + const jsize NS = outStrings ? env->GetArrayLength(outStrings) : 0; + const jsize NL = outLongs ? env->GetArrayLength(outLongs) : 0; + const jsize NR = outFloats ? env->GetArrayLength(outFloats) : 0; + + jint* formatData = env->GetIntArrayElements(format, 0); + jlong* longsData = outLongs ? + env->GetLongArrayElements(outLongs, 0) : NULL; + jfloat* floatsData = outFloats ? + env->GetFloatArrayElements(outFloats, 0) : NULL; + if (formatData == NULL || (NL > 0 && longsData == NULL) + || (NR > 0 && floatsData == NULL)) { + if (formatData != NULL) { + env->ReleaseIntArrayElements(format, formatData, 0); + } + if (longsData != NULL) { + env->ReleaseLongArrayElements(outLongs, longsData, 0); + } + if (floatsData != NULL) { + env->ReleaseFloatArrayElements(outFloats, floatsData, 0); + } + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return JNI_FALSE; + } + + jsize i = 0; + jsize di = 0; + + jboolean res = JNI_TRUE; + + for (jsize fi=0; fi<NF; fi++) { + const jint mode = formatData[fi]; + if ((mode&PROC_PARENS) != 0) { + i++; + } + const char term = (char)(mode&PROC_TERM_MASK); + const jsize start = i; + if (i >= len) { + res = JNI_FALSE; + break; + } + + jsize end = -1; + if ((mode&PROC_PARENS) != 0) { + while (buffer[i] != ')' && i < len) { + i++; + } + end = i; + i++; + } + while (buffer[i] != term && i < len) { + i++; + } + if (end < 0) { + end = i; + } + + if (i < len) { + i++; + if ((mode&PROC_COMBINE) != 0) { + while (buffer[i] == term && i < len) { + i++; + } + } + } + + //LOGI("Field %d: %d-%d dest=%d mode=0x%x\n", i, start, end, di, mode); + + if ((mode&(PROC_OUT_FLOAT|PROC_OUT_LONG|PROC_OUT_STRING)) != 0) { + char c = buffer[end]; + buffer[end] = 0; + if ((mode&PROC_OUT_FLOAT) != 0 && di < NR) { + char* end; + floatsData[di] = strtof(buffer+start, &end); + } + if ((mode&PROC_OUT_LONG) != 0 && di < NL) { + char* end; + longsData[di] = strtoll(buffer+start, &end, 10); + } + if ((mode&PROC_OUT_STRING) != 0 && di < NS) { + jstring str = env->NewStringUTF(buffer+start); + env->SetObjectArrayElement(outStrings, di, str); + } + buffer[end] = c; + di++; + } + } + + env->ReleaseIntArrayElements(format, formatData, 0); + if (longsData != NULL) { + env->ReleaseLongArrayElements(outLongs, longsData, 0); + } + if (floatsData != NULL) { + env->ReleaseFloatArrayElements(outFloats, floatsData, 0); + } + + return res; +} + +void android_os_Process_setApplicationObject(JNIEnv* env, jobject clazz, + jobject binderObject) +{ + if (binderObject == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + sp<IBinder> binder = ibinderForJavaObject(env, binderObject); +} + +void android_os_Process_sendSignal(JNIEnv* env, jobject clazz, jint pid, jint sig) +{ + if (pid > 0) { + LOGI("Sending signal. PID: %d SIG: %d", pid, sig); + kill(pid, sig); + } +} + +static jlong android_os_Process_getElapsedCpuTime(JNIEnv* env, jobject clazz) +{ + struct timespec ts; + + int res = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); + + if (res != 0) { + return (jlong) 0; + } + + nsecs_t when = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + return (jlong) nanoseconds_to_milliseconds(when); +} + +static jlong android_os_Process_getPss(JNIEnv* env, jobject clazz, jint pid) +{ + char filename[64]; + + snprintf(filename, sizeof(filename), "/proc/%d/smaps", pid); + + FILE * file = fopen(filename, "r"); + if (!file) { + return (jlong) -1; + } + + // Tally up all of the Pss from the various maps + char line[256]; + jlong pss = 0; + while (fgets(line, sizeof(line), file)) { + jlong v; + if (sscanf(line, "Pss: %lld kB", &v) == 1) { + pss += v; + } + } + + fclose(file); + + // Return the Pss value in bytes, not kilobytes + return pss * 1024; +} + +static const JNINativeMethod methods[] = { + {"myPid", "()I", (void*)android_os_Process_myPid}, + {"myTid", "()I", (void*)android_os_Process_myTid}, + {"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}, + {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority}, + {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority}, + {"setOomAdj", "(II)Z", (void*)android_os_Process_setOomAdj}, + {"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0}, + {"setUid", "(I)I", (void*)android_os_Process_setUid}, + {"setGid", "(I)I", (void*)android_os_Process_setGid}, + {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, + {"supportsProcesses", "()Z", (void*)android_os_Process_supportsProcesses}, + {"getFreeMemory", "()I", (void*)android_os_Process_getFreeMemory}, + {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines}, + {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids}, + {"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_readProcFile}, + {"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime}, + {"getPss", "(I)J", (void*)android_os_Process_getPss}, + //{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject}, +}; + +const char* const kProcessPathName = "android/os/Process"; + +int register_android_os_Process(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass(kProcessPathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Process"); + + return AndroidRuntime::registerNativeMethods( + env, kProcessPathName, + methods, NELEM(methods)); +} + diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp new file mode 100644 index 0000000..ffb271c --- /dev/null +++ b/core/jni/android_util_StringBlock.cpp @@ -0,0 +1,204 @@ +/* //device/libs/android_runtime/android_util_StringBlock.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "StringBlock" + +#include "jni.h" +#include <utils/misc.h> +#include <android_runtime/AndroidRuntime.h> +#include <utils/Log.h> + +#include <utils/ResourceTypes.h> + +#include <stdio.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + jclass npeClazz; + + npeClazz = env->FindClass(exc); + LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc); + + env->ThrowNew(npeClazz, msg); +} + +static jint android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz, + jbyteArray bArray, + jint off, jint len) +{ + if (bArray == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + jsize bLen = env->GetArrayLength(bArray); + if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { + doThrow(env, "java/lang/IndexOutOfBoundsException"); + return 0; + } + + jbyte* b = env->GetByteArrayElements(bArray, NULL); + ResStringPool* osb = new ResStringPool(b+off, len, true); + env->ReleaseByteArrayElements(bArray, b, 0); + + if (osb == NULL || osb->getError() != NO_ERROR) { + doThrow(env, "java/lang/IllegalArgumentException"); + return 0; + } + + return (jint)osb; +} + +static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz, + jint token) +{ + ResStringPool* osb = (ResStringPool*)token; + if (osb == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + return osb->size(); +} + +static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject clazz, + jint token, jint idx) +{ + ResStringPool* osb = (ResStringPool*)token; + if (osb == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + size_t len; + const char16_t* str = osb->stringAt(idx, &len); + if (str == NULL) { + doThrow(env, "java/lang/IndexOutOfBoundsException"); + return 0; + } + + return env->NewString((const jchar*)str, len); +} + +static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject clazz, + jint token, jint idx) +{ + ResStringPool* osb = (ResStringPool*)token; + if (osb == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return NULL; + } + + const ResStringPool_span* spans = osb->styleAt(idx); + if (spans == NULL) { + return NULL; + } + + const ResStringPool_span* pos = spans; + int num = 0; + while (pos->name.index != ResStringPool_span::END) { + num++; + pos++; + } + + if (num == 0) { + return NULL; + } + + jintArray array = env->NewIntArray((num*sizeof(ResStringPool_span))/sizeof(jint)); + if (array == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + return NULL; + } + + num = 0; + static const int numInts = sizeof(ResStringPool_span)/sizeof(jint); + while (spans->name.index != ResStringPool_span::END) { + env->SetIntArrayRegion(array, + num*numInts, numInts, + (jint*)spans); + spans++; + num++; + } + + return array; +} + +static jint android_content_StringBlock_nativeIndexOfString(JNIEnv* env, jobject clazz, + jint token, jstring str) +{ + ResStringPool* osb = (ResStringPool*)token; + if (osb == NULL || str == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + const char16_t* str16 = env->GetStringChars(str, NULL); + jsize strLen = env->GetStringLength(str); + + ssize_t idx = osb->indexOfString(str16, strLen); + + env->ReleaseStringChars(str, str16); + + return idx; +} + +static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz, + jint token) +{ + ResStringPool* osb = (ResStringPool*)token; + if (osb == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return; + } + + delete osb; +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gStringBlockMethods[] = { + /* name, signature, funcPtr */ + { "nativeCreate", "([BII)I", + (void*) android_content_StringBlock_nativeCreate }, + { "nativeGetSize", "(I)I", + (void*) android_content_StringBlock_nativeGetSize }, + { "nativeGetString", "(II)Ljava/lang/String;", + (void*) android_content_StringBlock_nativeGetString }, + { "nativeGetStyle", "(II)[I", + (void*) android_content_StringBlock_nativeGetStyle }, + { "nativeIndexOfString","(ILjava/lang/String;)I", + (void*) android_content_StringBlock_nativeIndexOfString }, + { "nativeDestroy", "(I)V", + (void*) android_content_StringBlock_nativeDestroy }, +}; + +int register_android_content_StringBlock(JNIEnv* env) +{ + return AndroidRuntime::registerNativeMethods(env, + "android/content/res/StringBlock", gStringBlockMethods, NELEM(gStringBlockMethods)); +} + +}; // namespace android + diff --git a/core/jni/android_util_XmlBlock.cpp b/core/jni/android_util_XmlBlock.cpp new file mode 100644 index 0000000..8887fdc --- /dev/null +++ b/core/jni/android_util_XmlBlock.cpp @@ -0,0 +1,426 @@ +/* //device/libs/android_runtime/android_util_XmlBlock.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "XmlBlock" + +#include "jni.h" +#include <utils/misc.h> +#include <android_runtime/AndroidRuntime.h> +#include <utils/AssetManager.h> +#include <utils/Log.h> + +#include <utils/ResourceTypes.h> + +#include <stdio.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + jclass npeClazz; + + npeClazz = env->FindClass(exc); + LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc); + + env->ThrowNew(npeClazz, msg); +} + +static jint android_content_XmlBlock_nativeCreate(JNIEnv* env, jobject clazz, + jbyteArray bArray, + jint off, jint len) +{ + if (bArray == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + jsize bLen = env->GetArrayLength(bArray); + if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { + doThrow(env, "java/lang/IndexOutOfBoundsException"); + return 0; + } + + jbyte* b = env->GetByteArrayElements(bArray, NULL); + ResXMLTree* osb = new ResXMLTree(b+off, len, true); + env->ReleaseByteArrayElements(bArray, b, 0); + + if (osb == NULL || osb->getError() != NO_ERROR) { + doThrow(env, "java/lang/IllegalArgumentException"); + return 0; + } + + return (jint)osb; +} + +static jint android_content_XmlBlock_nativeGetStringBlock(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLTree* osb = (ResXMLTree*)token; + if (osb == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + return (jint)&osb->getStrings(); +} + +static jint android_content_XmlBlock_nativeCreateParseState(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLTree* osb = (ResXMLTree*)token; + if (osb == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + ResXMLParser* st = new ResXMLParser(*osb); + if (st == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + return 0; + } + + st->restart(); + + return (jint)st; +} + +static jint android_content_XmlBlock_nativeNext(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + return ResXMLParser::END_DOCUMENT; + } + + do { + jint code = (jint)st->next(); + switch (code) { + case ResXMLParser::START_TAG: + return 2; + case ResXMLParser::END_TAG: + return 3; + case ResXMLParser::TEXT: + return 4; + case ResXMLParser::START_DOCUMENT: + return 0; + case ResXMLParser::END_DOCUMENT: + return 1; + case ResXMLParser::BAD_DOCUMENT: + goto bad; + } + } while (true); + +bad: + doThrow(env, "org/xmlpull/v1/XmlPullParserException", + "Corrupt XML binary file"); + return ResXMLParser::BAD_DOCUMENT; +} + +static jint android_content_XmlBlock_nativeGetNamespace(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + return -1; + } + + return (jint)st->getElementNamespaceID(); +} + +static jint android_content_XmlBlock_nativeGetName(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + return -1; + } + + return (jint)st->getElementNameID(); +} + +static jint android_content_XmlBlock_nativeGetText(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + return -1; + } + + return (jint)st->getTextID(); +} + +static jint android_content_XmlBlock_nativeGetLineNumber(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + return (jint)st->getLineNumber(); +} + +static jint android_content_XmlBlock_nativeGetAttributeCount(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + return (jint)st->getAttributeCount(); +} + +static jint android_content_XmlBlock_nativeGetAttributeNamespace(JNIEnv* env, jobject clazz, + jint token, jint idx) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + return (jint)st->getAttributeNamespaceID(idx); +} + +static jint android_content_XmlBlock_nativeGetAttributeName(JNIEnv* env, jobject clazz, + jint token, jint idx) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + return (jint)st->getAttributeNameID(idx); +} + +static jint android_content_XmlBlock_nativeGetAttributeResource(JNIEnv* env, jobject clazz, + jint token, jint idx) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + return (jint)st->getAttributeNameResID(idx); +} + +static jint android_content_XmlBlock_nativeGetAttributeDataType(JNIEnv* env, jobject clazz, + jint token, jint idx) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + return (jint)st->getAttributeDataType(idx); +} + +static jint android_content_XmlBlock_nativeGetAttributeData(JNIEnv* env, jobject clazz, + jint token, jint idx) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + return (jint)st->getAttributeData(idx); +} + +static jint android_content_XmlBlock_nativeGetAttributeStringValue(JNIEnv* env, jobject clazz, + jint token, jint idx) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + return (jint)st->getAttributeValueStringID(idx); +} + +static jint android_content_XmlBlock_nativeGetAttributeIndex(JNIEnv* env, jobject clazz, + jint token, + jstring ns, jstring name) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL || name == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + const char16_t* ns16 = NULL; + jsize nsLen = 0; + if (ns) { + ns16 = env->GetStringChars(ns, NULL); + nsLen = env->GetStringLength(ns); + } + + const char16_t* name16 = env->GetStringChars(name, NULL); + jsize nameLen = env->GetStringLength(name); + + jint idx = (jint)st->indexOfAttribute(ns16, nsLen, name16, nameLen); + + if (ns) { + env->ReleaseStringChars(ns, ns16); + } + env->ReleaseStringChars(name, name16); + + return idx; +} + +static jint android_content_XmlBlock_nativeGetIdAttribute(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + ssize_t idx = st->indexOfID(); + return idx >= 0 ? (jint)st->getAttributeValueStringID(idx) : -1; +} + +static jint android_content_XmlBlock_nativeGetClassAttribute(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + ssize_t idx = st->indexOfClass(); + return idx >= 0 ? (jint)st->getAttributeValueStringID(idx) : -1; +} + +static jint android_content_XmlBlock_nativeGetStyleAttribute(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return 0; + } + + ssize_t idx = st->indexOfStyle(); + if (idx < 0) { + return 0; + } + + Res_value value; + if (st->getAttributeValue(idx, &value) < 0) { + return 0; + } + + return value.dataType == value.TYPE_REFERENCE + || value.dataType == value.TYPE_ATTRIBUTE + ? value.data : 0; +} + +static void android_content_XmlBlock_nativeDestroyParseState(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLParser* st = (ResXMLParser*)token; + if (st == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return; + } + + delete st; +} + +static void android_content_XmlBlock_nativeDestroy(JNIEnv* env, jobject clazz, + jint token) +{ + ResXMLTree* osb = (ResXMLTree*)token; + if (osb == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return; + } + + delete osb; +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gXmlBlockMethods[] = { + /* name, signature, funcPtr */ + { "nativeCreate", "([BII)I", + (void*) android_content_XmlBlock_nativeCreate }, + { "nativeGetStringBlock", "(I)I", + (void*) android_content_XmlBlock_nativeGetStringBlock }, + { "nativeCreateParseState", "(I)I", + (void*) android_content_XmlBlock_nativeCreateParseState }, + { "nativeNext", "(I)I", + (void*) android_content_XmlBlock_nativeNext }, + { "nativeGetNamespace", "(I)I", + (void*) android_content_XmlBlock_nativeGetNamespace }, + { "nativeGetName", "(I)I", + (void*) android_content_XmlBlock_nativeGetName }, + { "nativeGetText", "(I)I", + (void*) android_content_XmlBlock_nativeGetText }, + { "nativeGetLineNumber", "(I)I", + (void*) android_content_XmlBlock_nativeGetLineNumber }, + { "nativeGetAttributeCount", "(I)I", + (void*) android_content_XmlBlock_nativeGetAttributeCount }, + { "nativeGetAttributeNamespace","(II)I", + (void*) android_content_XmlBlock_nativeGetAttributeNamespace }, + { "nativeGetAttributeName", "(II)I", + (void*) android_content_XmlBlock_nativeGetAttributeName }, + { "nativeGetAttributeResource", "(II)I", + (void*) android_content_XmlBlock_nativeGetAttributeResource }, + { "nativeGetAttributeDataType", "(II)I", + (void*) android_content_XmlBlock_nativeGetAttributeDataType }, + { "nativeGetAttributeData", "(II)I", + (void*) android_content_XmlBlock_nativeGetAttributeData }, + { "nativeGetAttributeStringValue", "(II)I", + (void*) android_content_XmlBlock_nativeGetAttributeStringValue }, + { "nativeGetAttributeIndex", "(ILjava/lang/String;Ljava/lang/String;)I", + (void*) android_content_XmlBlock_nativeGetAttributeIndex }, + { "nativeGetIdAttribute", "(I)I", + (void*) android_content_XmlBlock_nativeGetIdAttribute }, + { "nativeGetClassAttribute", "(I)I", + (void*) android_content_XmlBlock_nativeGetClassAttribute }, + { "nativeGetStyleAttribute", "(I)I", + (void*) android_content_XmlBlock_nativeGetStyleAttribute }, + { "nativeDestroyParseState", "(I)V", + (void*) android_content_XmlBlock_nativeDestroyParseState }, + { "nativeDestroy", "(I)V", + (void*) android_content_XmlBlock_nativeDestroy }, +}; + +int register_android_content_XmlBlock(JNIEnv* env) +{ + return AndroidRuntime::registerNativeMethods(env, + "android/content/res/XmlBlock", gXmlBlockMethods, NELEM(gXmlBlockMethods)); +} + +}; // namespace android + diff --git a/core/jni/android_view_Display.cpp b/core/jni/android_view_Display.cpp new file mode 100644 index 0000000..bb7b5ef --- /dev/null +++ b/core/jni/android_view_Display.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <assert.h> + +#include <ui/SurfaceComposerClient.h> +#include <ui/PixelFormat.h> +#include <ui/DisplayInfo.h> + +#include "jni.h" +#include <android_runtime/AndroidRuntime.h> +#include <utils/misc.h> + +// ---------------------------------------------------------------------------- + +namespace android { + +// ---------------------------------------------------------------------------- + +struct offsets_t { + jfieldID display; + jfieldID pixelFormat; + jfieldID fps; + jfieldID density; + jfieldID xdpi; + jfieldID ydpi; +}; +static offsets_t offsets; + +static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + jclass npeClazz = env->FindClass(exc); + env->ThrowNew(npeClazz, msg); +} + +// ---------------------------------------------------------------------------- + +static void android_view_Display_init( + JNIEnv* env, jobject clazz, jint dpy) +{ + DisplayInfo info; + status_t err = SurfaceComposerClient::getDisplayInfo(DisplayID(dpy), &info); + if (err < 0) { + doThrow(env, "java/lang/IllegalArgumentException"); + return; + } + env->SetIntField(clazz, offsets.pixelFormat,info.pixelFormatInfo.format); + env->SetFloatField(clazz, offsets.fps, info.fps); + env->SetFloatField(clazz, offsets.density, info.density); + env->SetFloatField(clazz, offsets.xdpi, info.xdpi); + env->SetFloatField(clazz, offsets.ydpi, info.ydpi); +} + +static jint android_view_Display_getWidth( + JNIEnv* env, jobject clazz) +{ + DisplayID dpy = env->GetIntField(clazz, offsets.display); + return SurfaceComposerClient::getDisplayWidth(dpy); +} + +static jint android_view_Display_getHeight( + JNIEnv* env, jobject clazz) +{ + DisplayID dpy = env->GetIntField(clazz, offsets.display); + return SurfaceComposerClient::getDisplayHeight(dpy); +} + +static jint android_view_Display_getOrientation( + JNIEnv* env, jobject clazz) +{ + DisplayID dpy = env->GetIntField(clazz, offsets.display); + return SurfaceComposerClient::getDisplayOrientation(dpy); +} + +static jint android_view_Display_getDisplayCount( + JNIEnv* env, jclass clazz) +{ + return SurfaceComposerClient::getNumberOfDisplays(); +} + +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/view/Display"; + +static void nativeClassInit(JNIEnv* env, jclass clazz); + +static JNINativeMethod gMethods[] = { + { "nativeClassInit", "()V", + (void*)nativeClassInit }, + { "getDisplayCount", "()I", + (void*)android_view_Display_getDisplayCount }, + { "init", "(I)V", + (void*)android_view_Display_init }, + { "getWidth", "()I", + (void*)android_view_Display_getWidth }, + { "getHeight", "()I", + (void*)android_view_Display_getHeight }, + { "getOrientation", "()I", + (void*)android_view_Display_getOrientation } +}; + +void nativeClassInit(JNIEnv* env, jclass clazz) +{ + offsets.display = env->GetFieldID(clazz, "mDisplay", "I"); + offsets.pixelFormat = env->GetFieldID(clazz, "mPixelFormat", "I"); + offsets.fps = env->GetFieldID(clazz, "mRefreshRate", "F"); + offsets.density = env->GetFieldID(clazz, "mDensity", "F"); + offsets.xdpi = env->GetFieldID(clazz, "mDpiX", "F"); + offsets.ydpi = env->GetFieldID(clazz, "mDpiY", "F"); +} + +int register_android_view_Display(JNIEnv* env) +{ + return AndroidRuntime::registerNativeMethods(env, + kClassPathName, gMethods, NELEM(gMethods)); +} + +}; + diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp new file mode 100644 index 0000000..f09bd4c --- /dev/null +++ b/core/jni/android_view_Surface.cpp @@ -0,0 +1,619 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#include "android_util_Binder.h" + +#include <ui/SurfaceComposerClient.h> +#include <ui/Region.h> +#include <ui/Rect.h> + +#include <SkCanvas.h> +#include <SkBitmap.h> + +#include "jni.h" +#include <android_runtime/AndroidRuntime.h> +#include <utils/misc.h> + + +// ---------------------------------------------------------------------------- + +namespace android { + +// ---------------------------------------------------------------------------- + +static const char* const OutOfResourcesException = + "android/view/Surface$OutOfResourcesException"; + +struct sso_t { + jfieldID client; +}; +static sso_t sso; + +struct so_t { + jfieldID surface; + jfieldID saveCount; + jfieldID canvas; +}; +static so_t so; + +struct ro_t { + jfieldID l; + jfieldID t; + jfieldID r; + jfieldID b; +}; +static ro_t ro; + +struct po_t { + jfieldID x; + jfieldID y; +}; +static po_t po; + +struct co_t { + jfieldID surfaceFormat; +}; +static co_t co; + +struct no_t { + jfieldID native_canvas; + jfieldID native_region; + jfieldID native_parcel; +}; +static no_t no; + + +static __attribute__((noinline)) +void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + if (!env->ExceptionOccurred()) { + jclass npeClazz = env->FindClass(exc); + env->ThrowNew(npeClazz, msg); + } +} + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +static void SurfaceSession_init(JNIEnv* env, jobject clazz) +{ + sp<SurfaceComposerClient> client = new SurfaceComposerClient; + client->incStrong(clazz); + env->SetIntField(clazz, sso.client, (int)client.get()); +} + +static void SurfaceSession_destroy(JNIEnv* env, jobject clazz) +{ + SurfaceComposerClient* client = + (SurfaceComposerClient*)env->GetIntField(clazz, sso.client); + if (client != 0) { + client->decStrong(clazz); + env->SetIntField(clazz, sso.client, 0); + } +} + +static void SurfaceSession_kill(JNIEnv* env, jobject clazz) +{ + SurfaceComposerClient* client = + (SurfaceComposerClient*)env->GetIntField(clazz, sso.client); + if (client != 0) { + client->dispose(); + client->decStrong(clazz); + env->SetIntField(clazz, sso.client, 0); + } +} + +// ---------------------------------------------------------------------------- + +static sp<Surface> getSurface(JNIEnv* env, jobject clazz) +{ + Surface* const p = (Surface*)env->GetIntField(clazz, so.surface); + return sp<Surface>(p); +} + +static void setSurface(JNIEnv* env, jobject clazz, const sp<Surface>& surface) +{ + Surface* const p = (Surface*)env->GetIntField(clazz, so.surface); + if (surface.get()) { + surface->incStrong(clazz); + } + if (p) { + p->decStrong(clazz); + } + env->SetIntField(clazz, so.surface, (int)surface.get()); +} + +// ---------------------------------------------------------------------------- + +static void Surface_init( + JNIEnv* env, jobject clazz, + jobject session, jint pid, jint dpy, jint w, jint h, jint format, jint flags) +{ + if (session == NULL) { + doThrow(env, "java/lang/NullPointerException"); + return; + } + + SurfaceComposerClient* client = + (SurfaceComposerClient*)env->GetIntField(session, sso.client); + + sp<Surface> surface(client->createSurface(pid, dpy, w, h, format, flags)); + if (surface == 0) { + doThrow(env, OutOfResourcesException); + return; + } + setSurface(env, clazz, surface); +} + +static void Surface_initParcel(JNIEnv* env, jobject clazz, jobject argParcel) +{ + Parcel* parcel = (Parcel*)env->GetIntField(argParcel, no.native_parcel); + if (parcel == NULL) { + doThrow(env, "java/lang/NullPointerException", NULL); + return; + } + const sp<Surface>& rhs = Surface::readFromParcel(parcel); + setSurface(env, clazz, rhs); +} + +static void Surface_clear(JNIEnv* env, jobject clazz, uintptr_t *ostack) +{ + setSurface(env, clazz, 0); +} + +static jboolean Surface_isValid(JNIEnv* env, jobject clazz) +{ + const sp<Surface>& surface = getSurface(env, clazz); + return surface->isValid() ? JNI_TRUE : JNI_FALSE; +} + +static inline SkBitmap::Config convertPixelFormat(PixelFormat format) +{ + /* note: if PIXEL_FORMAT_XRGB_8888 means that all alpha bytes are 0xFF, then + we can map to SkBitmap::kARGB_8888_Config, and optionally call + bitmap.setIsOpaque(true) on the resulting SkBitmap (as an accelerator) + */ + switch (format) { + case PIXEL_FORMAT_RGBA_8888: return SkBitmap::kARGB_8888_Config; + case PIXEL_FORMAT_RGBA_4444: return SkBitmap::kARGB_4444_Config; + case PIXEL_FORMAT_RGB_565: return SkBitmap::kRGB_565_Config; + case PIXEL_FORMAT_A_8: return SkBitmap::kA8_Config; + default: return SkBitmap::kNo_Config; + } +} + +static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (!surface->isValid()) + return 0; + + // get dirty region + Region dirtyRegion; + if (dirtyRect) { + Rect dirty; + dirty.left = env->GetIntField(dirtyRect, ro.l); + dirty.top = env->GetIntField(dirtyRect, ro.t); + dirty.right = env->GetIntField(dirtyRect, ro.r); + dirty.bottom= env->GetIntField(dirtyRect, ro.b); + dirtyRegion.set(dirty); + } else { + dirtyRegion.set(Rect(0x3FFF,0x3FFF)); + } + + Surface::SurfaceInfo info; + status_t err = surface->lock(&info, &dirtyRegion); + if (err < 0) { + const char* const exception = (err == NO_MEMORY) ? + OutOfResourcesException : + "java/lang/IllegalArgumentException"; + doThrow(env, exception, NULL); + return 0; + } + + // Associate a SkCanvas object to this surface + jobject canvas = env->GetObjectField(clazz, so.canvas); + env->SetIntField(canvas, co.surfaceFormat, info.format); + + SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas); + SkBitmap bitmap; + bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, info.bpr); + if (info.w > 0 && info.h > 0) { + bitmap.setPixels(info.bits); + } else { + // be safe with an empty bitmap. + bitmap.setPixels(NULL); + } + nativeCanvas->setBitmapDevice(bitmap); + nativeCanvas->clipRegion(dirtyRegion.toSkRegion()); + + int saveCount = nativeCanvas->save(); + env->SetIntField(clazz, so.saveCount, saveCount); + + return canvas; +} + +static void Surface_unlockCanvasAndPost( + JNIEnv* env, jobject clazz, jobject argCanvas) +{ + jobject canvas = env->GetObjectField(clazz, so.canvas); + if (canvas != argCanvas) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + const sp<Surface>& surface = getSurface(env, clazz); + if (!surface->isValid()) + return; + + // detach the canvas from the surface + SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas); + int saveCount = env->GetIntField(clazz, so.saveCount); + nativeCanvas->restoreToCount(saveCount); + nativeCanvas->setBitmapDevice(SkBitmap()); + env->SetIntField(clazz, so.saveCount, 0); + + // unlock surface + status_t err = surface->unlockAndPost(); + if (err < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } +} + +static void Surface_unlockCanvas( + JNIEnv* env, jobject clazz, jobject argCanvas) +{ + jobject canvas = env->GetObjectField(clazz, so.canvas); + if (canvas != argCanvas) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + const sp<Surface>& surface = getSurface(env, clazz); + if (!surface->isValid()) + return; + + status_t err = surface->unlock(); + if (err < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + return; + } + SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas); + int saveCount = env->GetIntField(clazz, so.saveCount); + nativeCanvas->restoreToCount(saveCount); + nativeCanvas->setBitmapDevice(SkBitmap()); + env->SetIntField(clazz, so.saveCount, 0); +} + +static void Surface_openTransaction( + JNIEnv* env, jobject clazz) +{ + SurfaceComposerClient::openGlobalTransaction(); +} + +static void Surface_closeTransaction( + JNIEnv* env, jobject clazz) +{ + SurfaceComposerClient::closeGlobalTransaction(); +} + +static void Surface_setOrientation( + JNIEnv* env, jobject clazz, jint display, jint orientation) +{ + int err = SurfaceComposerClient::setOrientation(display, orientation); + if (err < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } +} + +static void Surface_freezeDisplay( + JNIEnv* env, jobject clazz, jint display) +{ + int err = SurfaceComposerClient::freezeDisplay(display, 0); + if (err < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } +} + +static void Surface_unfreezeDisplay( + JNIEnv* env, jobject clazz, jint display) +{ + int err = SurfaceComposerClient::unfreezeDisplay(display, 0); + if (err < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } +} + +static void Surface_setLayer( + JNIEnv* env, jobject clazz, jint zorder) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + if (surface->setLayer(zorder) < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_setPosition( + JNIEnv* env, jobject clazz, jint x, jint y) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + if (surface->setPosition(x, y) < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_setSize( + JNIEnv* env, jobject clazz, jint w, jint h) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + if (surface->setSize(w, h) < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_hide( + JNIEnv* env, jobject clazz) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + if (surface->hide() < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_show( + JNIEnv* env, jobject clazz) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + if (surface->show() < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_freeze( + JNIEnv* env, jobject clazz) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + if (surface->freeze() < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_unfreeze( + JNIEnv* env, jobject clazz) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + if (surface->unfreeze() < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_setFlags( + JNIEnv* env, jobject clazz, jint flags, jint mask) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + if (surface->setFlags(flags, mask) < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_setTransparentRegion( + JNIEnv* env, jobject clazz, jobject argRegion) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + SkRegion* nativeRegion = (SkRegion*)env->GetIntField(argRegion, no.native_region); + if (surface->setTransparentRegionHint(Region(*nativeRegion)) < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_setAlpha( + JNIEnv* env, jobject clazz, jfloat alpha) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + if (surface->setAlpha(alpha) < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_setMatrix( + JNIEnv* env, jobject clazz, + jfloat dsdx, jfloat dtdx, jfloat dsdy, jfloat dtdy) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + if (surface->setMatrix(dsdx, dtdx, dsdy, dtdy) < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_setFreezeTint( + JNIEnv* env, jobject clazz, + jint tint) +{ + const sp<Surface>& surface = getSurface(env, clazz); + if (surface->isValid()) { + if (surface->setFreezeTint(tint) < 0) { + doThrow(env, "java/lang/IllegalArgumentException", NULL); + } + } +} + +static void Surface_copyFrom( + JNIEnv* env, jobject clazz, jobject other) +{ + if (clazz == other) + return; + + if (other == NULL) { + doThrow(env, "java/lang/NullPointerException", NULL); + return; + } + + const sp<Surface>& surface = getSurface(env, clazz); + const sp<Surface>& rhs = getSurface(env, other); + if (!Surface::isSameSurface(surface, rhs)) { + // we reassign the surface only if it's a different one + // otherwise we would loose our client-side state. + setSurface(env, clazz, rhs->dup()); + } +} + + +static void Surface_readFromParcel( + JNIEnv* env, jobject clazz, jobject argParcel) +{ + Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel); + if (parcel == NULL) { + doThrow(env, "java/lang/NullPointerException", NULL); + return; + } + + const sp<Surface>& surface = getSurface(env, clazz); + const sp<Surface>& rhs = Surface::readFromParcel(parcel); + if (!Surface::isSameSurface(surface, rhs)) { + // we reassign the surface only if it's a different one + // otherwise we would loose our client-side state. + setSurface(env, clazz, rhs); + } +} + +static void Surface_writeToParcel( + JNIEnv* env, jobject clazz, jobject argParcel, jint flags) +{ + Parcel* parcel = (Parcel*)env->GetIntField( + argParcel, no.native_parcel); + + if (parcel == NULL) { + doThrow(env, "java/lang/NullPointerException", NULL); + return; + } + + const sp<Surface>& surface = getSurface(env, clazz); + Surface::writeToParcel(surface, parcel); +} + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +const char* const kSurfaceSessionClassPathName = "android/view/SurfaceSession"; +const char* const kSurfaceClassPathName = "android/view/Surface"; +static void nativeClassInit(JNIEnv* env, jclass clazz); + +static JNINativeMethod gSurfaceSessionMethods[] = { + {"init", "()V", (void*)SurfaceSession_init }, + {"destroy", "()V", (void*)SurfaceSession_destroy }, + {"kill", "()V", (void*)SurfaceSession_kill }, +}; + +static JNINativeMethod gSurfaceMethods[] = { + {"nativeClassInit", "()V", (void*)nativeClassInit }, + {"init", "(Landroid/view/SurfaceSession;IIIIII)V", (void*)Surface_init }, + {"init", "(Landroid/os/Parcel;)V", (void*)Surface_initParcel }, + {"clear", "()V", (void*)Surface_clear }, + {"copyFrom", "(Landroid/view/Surface;)V", (void*)Surface_copyFrom }, + {"isValid", "()Z", (void*)Surface_isValid }, + {"lockCanvasNative", "(Landroid/graphics/Rect;)Landroid/graphics/Canvas;", (void*)Surface_lockCanvas }, + {"unlockCanvasAndPost", "(Landroid/graphics/Canvas;)V", (void*)Surface_unlockCanvasAndPost }, + {"unlockCanvas", "(Landroid/graphics/Canvas;)V", (void*)Surface_unlockCanvas }, + {"openTransaction", "()V", (void*)Surface_openTransaction }, + {"closeTransaction", "()V", (void*)Surface_closeTransaction }, + {"setOrientation", "(II)V", (void*)Surface_setOrientation }, + {"freezeDisplay", "(I)V", (void*)Surface_freezeDisplay }, + {"unfreezeDisplay", "(I)V", (void*)Surface_unfreezeDisplay }, + {"setLayer", "(I)V", (void*)Surface_setLayer }, + {"setPosition", "(II)V",(void*)Surface_setPosition }, + {"setSize", "(II)V",(void*)Surface_setSize }, + {"hide", "()V", (void*)Surface_hide }, + {"show", "()V", (void*)Surface_show }, + {"freeze", "()V", (void*)Surface_freeze }, + {"unfreeze", "()V", (void*)Surface_unfreeze }, + {"setFlags", "(II)V",(void*)Surface_setFlags }, + {"setTransparentRegionHint","(Landroid/graphics/Region;)V", (void*)Surface_setTransparentRegion }, + {"setAlpha", "(F)V", (void*)Surface_setAlpha }, + {"setMatrix", "(FFFF)V", (void*)Surface_setMatrix }, + {"setFreezeTint", "(I)V", (void*)Surface_setFreezeTint }, + {"readFromParcel", "(Landroid/os/Parcel;)V", (void*)Surface_readFromParcel }, + {"writeToParcel", "(Landroid/os/Parcel;I)V", (void*)Surface_writeToParcel }, +}; + +void nativeClassInit(JNIEnv* env, jclass clazz) +{ + so.surface = env->GetFieldID(clazz, "mSurface", "I"); + so.saveCount = env->GetFieldID(clazz, "mSaveCount", "I"); + so.canvas = env->GetFieldID(clazz, "mCanvas", "Landroid/graphics/Canvas;"); + + jclass surfaceSession = env->FindClass("android/view/SurfaceSession"); + sso.client = env->GetFieldID(surfaceSession, "mClient", "I"); + + jclass canvas = env->FindClass("android/graphics/Canvas"); + no.native_canvas = env->GetFieldID(canvas, "mNativeCanvas", "I"); + co.surfaceFormat = env->GetFieldID(canvas, "mSurfaceFormat", "I"); + + jclass region = env->FindClass("android/graphics/Region"); + no.native_region = env->GetFieldID(region, "mNativeRegion", "I"); + + jclass parcel = env->FindClass("android/os/Parcel"); + no.native_parcel = env->GetFieldID(parcel, "mObject", "I"); + + jclass rect = env->FindClass("android/graphics/Rect"); + ro.l = env->GetFieldID(rect, "left", "I"); + ro.t = env->GetFieldID(rect, "top", "I"); + ro.r = env->GetFieldID(rect, "right", "I"); + ro.b = env->GetFieldID(rect, "bottom", "I"); + + jclass point = env->FindClass("android/graphics/Point"); + po.x = env->GetFieldID(point, "x", "I"); + po.y = env->GetFieldID(point, "y", "I"); +} + +int register_android_view_Surface(JNIEnv* env) +{ + int err; + err = AndroidRuntime::registerNativeMethods(env, kSurfaceSessionClassPathName, + gSurfaceSessionMethods, NELEM(gSurfaceSessionMethods)); + + err |= AndroidRuntime::registerNativeMethods(env, kSurfaceClassPathName, + gSurfaceMethods, NELEM(gSurfaceMethods)); + return err; +} + +}; + diff --git a/core/jni/android_view_ViewRoot.cpp b/core/jni/android_view_ViewRoot.cpp new file mode 100644 index 0000000..843d293 --- /dev/null +++ b/core/jni/android_view_ViewRoot.cpp @@ -0,0 +1,98 @@ +/* + * 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. + */ + +#include <stdio.h> +#include <assert.h> + +#include <graphics/SkCanvas.h> +#include <graphics/SkDevice.h> +#include <graphics/SkGLCanvas.h> +#include <graphics/SkPaint.h> +#include "GraphicsJNI.h" + +#include "jni.h" +#include <android_runtime/AndroidRuntime.h> +#include <utils/misc.h> + +// ---------------------------------------------------------------------------- + +namespace android { + +static int gPrevDur; + +static void android_view_ViewRoot_showFPS(JNIEnv* env, jobject, jobject jcanvas, + jint dur) { + NPE_CHECK_RETURN_VOID(env, jcanvas); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); + const SkBitmap& bm = canvas->getDevice()->accessBitmap(false); + int height = bm.height(); + SkScalar bot = SkIntToScalar(height); + + if (height < 200) { + return; + } + + SkMatrix m; + SkRect r; + SkPaint p; + char str[4]; + + dur = (gPrevDur + dur) >> 1; + gPrevDur = dur; + + dur = 1000 / dur; + str[3] = (char)('0' + dur % 10); dur /= 10; + str[2] = (char)('0' + dur % 10); dur /= 10; + str[1] = (char)('0' + dur % 10); dur /= 10; + str[0] = (char)('0' + dur % 10); + + m.reset(); + r.set(0, bot-SkIntToScalar(10), SkIntToScalar(26), bot); + p.setAntiAlias(true); + p.setTextSize(SkIntToScalar(10)); + + canvas->save(); + canvas->setMatrix(m); + canvas->clipRect(r, SkRegion::kReplace_Op); + p.setColor(SK_ColorWHITE); + canvas->drawPaint(p); + p.setColor(SK_ColorBLACK); + canvas->drawText(str, 4, SkIntToScalar(1), bot - SK_Scalar1, p); + canvas->restore(); +} + +static void android_view_ViewRoot_abandonGlCaches(JNIEnv* env, jobject) { + SkGLCanvas::AbandonAllTextures(); +} + +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/view/ViewRoot"; + +static JNINativeMethod gMethods[] = { + { "nativeShowFPS", "(Landroid/graphics/Canvas;I)V", + (void*)android_view_ViewRoot_showFPS }, + { "nativeAbandonGlCaches", "()V", + (void*)android_view_ViewRoot_abandonGlCaches } +}; + +int register_android_view_ViewRoot(JNIEnv* env) { + return AndroidRuntime::registerNativeMethods(env, + kClassPathName, gMethods, NELEM(gMethods)); +} + +}; + diff --git a/core/jni/com_android_internal_graphics_NativeUtils.cpp b/core/jni/com_android_internal_graphics_NativeUtils.cpp new file mode 100644 index 0000000..0829532 --- /dev/null +++ b/core/jni/com_android_internal_graphics_NativeUtils.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AWT" + +#include "jni.h" +#include "JNIHelp.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPicture.h" +#include "SkTemplates.h" + +namespace android +{ + +static jclass class_fileDescriptor; +static jfieldID field_fileDescriptor_descriptor; +static jmethodID method_fileDescriptor_init; + + +static jboolean scrollRect(JNIEnv* env, jobject graphics2D, jobject canvas, jobject rect, int dx, int dy) { + if (canvas == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return false; + } + + SkIRect src, *srcPtr = NULL; + if (NULL != rect) { + GraphicsJNI::jrect_to_irect(env, rect, &src); + srcPtr = &src; + } + SkCanvas* c = GraphicsJNI::getNativeCanvas(env, canvas); + const SkBitmap& bitmap = c->getDevice()->accessBitmap(true); + return bitmap.scrollRect(srcPtr, dx, dy, NULL); +} + +static JNINativeMethod method_table[] = { + { "nativeScrollRect", + "(Landroid/graphics/Canvas;Landroid/graphics/Rect;II)Z", + (void*)scrollRect} +}; + +int register_com_android_internal_graphics_NativeUtils(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods( + env, "com/android/internal/graphics/NativeUtils", + method_table, NELEM(method_table)); +} + +} diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp new file mode 100644 index 0000000..ada4dd3 --- /dev/null +++ b/core/jni/com_android_internal_os_ZygoteInit.cpp @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Zygote" + +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <utils/misc.h> +#include <errno.h> +#include <sys/select.h> + +#include "jni.h" +#include <JNIHelp.h> +#include "android_runtime/AndroidRuntime.h" + +#ifdef HAVE_ANDROID_OS +#include <linux/capability.h> +#include <linux/prctl.h> +#include <sys/prctl.h> +extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); +extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); +#endif + + +namespace android { + +/* + * In class com.android.internal.os.ZygoteInit: + * private static native boolean setreuid(int ruid, int euid) + */ +static jint com_android_internal_os_ZygoteInit_setreuid( + JNIEnv* env, jobject clazz, jint ruid, jint euid) +{ + int err; + + errno = 0; + err = setreuid(ruid, euid); + + //LOGI("setreuid(%d,%d) err %d errno %d", ruid, euid, err, errno); + + return errno; +} + +/* + * In class com.android.internal.os.ZygoteInit: + * private static native int setregid(int rgid, int egid) + */ +static jint com_android_internal_os_ZygoteInit_setregid( + JNIEnv* env, jobject clazz, jint rgid, jint egid) +{ + int err; + + errno = 0; + err = setregid(rgid, egid); + + //LOGI("setregid(%d,%d) err %d errno %d", rgid, egid, err, errno); + + return errno; +} + +/* + * In class com.android.internal.os.ZygoteInit: + * private static native int setpgid(int rgid, int egid) + */ +static jint com_android_internal_os_ZygoteInit_setpgid( + JNIEnv* env, jobject clazz, jint pid, jint pgid) +{ + int err; + + errno = 0; + + err = setpgid(pid, pgid); + + return errno; +} + +/* + * In class com.android.internal.os.ZygoteInit: + * private static native int getpgid(int pid) + */ +static jint com_android_internal_os_ZygoteInit_getpgid( + JNIEnv* env, jobject clazz, jint pid) +{ + pid_t ret; + ret = getpgid(pid); + + if (ret < 0) { + jniThrowIOException(env, errno); + } + + return ret; +} + +static void com_android_internal_os_ZygoteInit_reopenStdio(JNIEnv* env, + jobject clazz, jobject in, jobject out, jobject errfd) +{ + int fd; + int err; + + fd = jniGetFDFromFileDescriptor(env, in); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + do { + err = dup2(fd, STDIN_FILENO); + } while (err < 0 && errno == EINTR); + + fd = jniGetFDFromFileDescriptor(env, out); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + do { + err = dup2(fd, STDOUT_FILENO); + } while (err < 0 && errno == EINTR); + + fd = jniGetFDFromFileDescriptor(env, errfd); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + do { + err = dup2(fd, STDERR_FILENO); + } while (err < 0 && errno == EINTR); +} + +static void com_android_internal_os_ZygoteInit_closeDescriptor(JNIEnv* env, + jobject clazz, jobject descriptor) +{ + int fd; + int err; + + fd = jniGetFDFromFileDescriptor(env, descriptor); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + do { + err = close(fd); + } while (err < 0 && errno == EINTR); + + if (err < 0) { + jniThrowIOException(env, errno); + return; + } +} + +static void com_android_internal_os_ZygoteInit_setCloseOnExec (JNIEnv *env, + jobject clazz, jobject descriptor, jboolean flag) +{ + int fd; + int err; + int fdFlags; + + fd = jniGetFDFromFileDescriptor(env, descriptor); + + if (env->ExceptionOccurred() != NULL) { + return; + } + + fdFlags = fcntl(fd, F_GETFD); + + if (fdFlags < 0) { + jniThrowIOException(env, errno); + return; + } + + if (flag) { + fdFlags |= FD_CLOEXEC; + } else { + fdFlags &= ~FD_CLOEXEC; + } + + err = fcntl(fd, F_SETFD, fdFlags); + + if (err < 0) { + jniThrowIOException(env, errno); + return; + } +} + +static void com_android_internal_os_ZygoteInit_setCapabilities (JNIEnv *env, + jobject clazz, jlong permitted, jlong effective) +{ +#ifdef HAVE_ANDROID_OS + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + int err; + + memset (&capheader, 0, sizeof(capheader)); + memset (&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + // As of this writing, capdata is __u32, but that's expected + // to change... + capdata.effective = effective; + capdata.permitted = permitted; + + err = capset (&capheader, &capdata); + + if (err < 0) { + jniThrowIOException(env, errno); + return; + } +#endif /* HAVE_ANDROID_OS */ +} + +static jlong com_android_internal_os_ZygoteInit_capgetPermitted (JNIEnv *env, + jobject clazz, jint pid) +{ +#ifndef HAVE_ANDROID_OS + return (jlong)0; +#else + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + int err; + + memset (&capheader, 0, sizeof(capheader)); + memset (&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = pid; + + err = capget (&capheader, &capdata); + + if (err < 0) { + jniThrowIOException(env, errno); + return 0; + } + + return (jlong) capdata.permitted; +#endif /* HAVE_ANDROID_OS */ +} + +static jint com_android_internal_os_ZygoteInit_selectReadable ( + JNIEnv *env, jobject clazz, jobjectArray fds) +{ + if (fds == NULL) { + jniThrowException(env, "java/lang/NullPointerException", + "fds == null"); + return -1; + } + + jsize length = env->GetArrayLength(fds); + fd_set fdset; + + if (env->ExceptionOccurred() != NULL) { + return -1; + } + + FD_ZERO(&fdset); + + int nfds = 0; + for (jsize i = 0; i < length; i++) { + jobject fdObj = env->GetObjectArrayElement(fds, i); + if (env->ExceptionOccurred() != NULL) { + return -1; + } + if (fdObj == NULL) { + continue; + } + int fd = jniGetFDFromFileDescriptor(env, fdObj); + if (env->ExceptionOccurred() != NULL) { + return -1; + } + + FD_SET(fd, &fdset); + + if (fd >= nfds) { + nfds = fd + 1; + } + } + + int err; + do { + err = select (nfds, &fdset, NULL, NULL, NULL); + } while (err < 0 && errno == EINTR); + + if (err < 0) { + jniThrowIOException(env, errno); + return -1; + } + + for (jsize i = 0; i < length; i++) { + jobject fdObj = env->GetObjectArrayElement(fds, i); + if (env->ExceptionOccurred() != NULL) { + return -1; + } + if (fdObj == NULL) { + continue; + } + int fd = jniGetFDFromFileDescriptor(env, fdObj); + if (env->ExceptionOccurred() != NULL) { + return -1; + } + if (FD_ISSET(fd, &fdset)) { + return (jint)i; + } + } + return -1; +} + +static jobject com_android_internal_os_ZygoteInit_createFileDescriptor ( + JNIEnv *env, jobject clazz, jint fd) +{ + return jniCreateFileDescriptor(env, fd); +} + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "setreuid", "(II)I", + (void*) com_android_internal_os_ZygoteInit_setreuid }, + { "setregid", "(II)I", + (void*) com_android_internal_os_ZygoteInit_setregid }, + { "setpgid", "(II)I", + (void *) com_android_internal_os_ZygoteInit_setpgid }, + { "getpgid", "(I)I", + (void *) com_android_internal_os_ZygoteInit_getpgid }, + { "reopenStdio", + "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;" + "Ljava/io/FileDescriptor;)V", + (void *) com_android_internal_os_ZygoteInit_reopenStdio}, + { "closeDescriptor", "(Ljava/io/FileDescriptor;)V", + (void *) com_android_internal_os_ZygoteInit_closeDescriptor}, + { "setCloseOnExec", "(Ljava/io/FileDescriptor;Z)V", + (void *) com_android_internal_os_ZygoteInit_setCloseOnExec}, + { "setCapabilities", "(JJ)V", + (void *) com_android_internal_os_ZygoteInit_setCapabilities }, + { "capgetPermitted", "(I)J", + (void *) com_android_internal_os_ZygoteInit_capgetPermitted }, + { "selectReadable", "([Ljava/io/FileDescriptor;)I", + (void *) com_android_internal_os_ZygoteInit_selectReadable }, + { "createFileDescriptor", "(I)Ljava/io/FileDescriptor;", + (void *) com_android_internal_os_ZygoteInit_createFileDescriptor } +}; +int register_com_android_internal_os_ZygoteInit(JNIEnv* env) +{ + return AndroidRuntime::registerNativeMethods(env, + "com/android/internal/os/ZygoteInit", gMethods, NELEM(gMethods)); +} + +}; // namespace android + diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp new file mode 100644 index 0000000..af03016 --- /dev/null +++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp @@ -0,0 +1,475 @@ +/* +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <android_runtime/AndroidRuntime.h> +#include <utils/misc.h> + +#include <GLES/egl.h> +#include <GLES/gl.h> + +#include <ui/EGLNativeWindowSurface.h> +#include <ui/Surface.h> +#include <graphics/SkBitmap.h> +#include <graphics/SkPixelRef.h> + +namespace android { + +static jclass gDisplay_class; +static jclass gContext_class; +static jclass gSurface_class; +static jclass gConfig_class; + +static jmethodID gConfig_ctorID; + +static jfieldID gDisplay_EGLDisplayFieldID; +static jfieldID gContext_EGLContextFieldID; +static jfieldID gSurface_EGLSurfaceFieldID; +static jfieldID gSurface_NativePixelRefFieldID; +static jfieldID gConfig_EGLConfigFieldID; +static jfieldID gSurface_SurfaceFieldID; +static jfieldID gBitmap_NativeBitmapFieldID; + +static __attribute__((noinline)) +void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + jclass npeClazz = env->FindClass(exc); + env->ThrowNew(npeClazz, msg); +} + +static __attribute__((noinline)) +bool hasException(JNIEnv *env) { + if (env->ExceptionCheck() != 0) { + env->ExceptionDescribe(); + return true; + } + return false; +} + +static __attribute__((noinline)) +jclass make_globalref(JNIEnv* env, const char classname[]) { + jclass c = env->FindClass(classname); + return (jclass)env->NewGlobalRef(c); +} + +static inline EGLDisplay getDisplay(JNIEnv* env, jobject o) { + if (!o) return EGL_NO_DISPLAY; + return (EGLDisplay)env->GetIntField(o, gDisplay_EGLDisplayFieldID); +} +static inline EGLSurface getSurface(JNIEnv* env, jobject o) { + if (!o) return EGL_NO_SURFACE; + return (EGLSurface)env->GetIntField(o, gSurface_EGLSurfaceFieldID); +} +static inline EGLContext getContext(JNIEnv* env, jobject o) { + if (!o) return EGL_NO_CONTEXT; + return (EGLContext)env->GetIntField(o, gContext_EGLContextFieldID); +} +static inline EGLConfig getConfig(JNIEnv* env, jobject o) { + if (!o) return 0; + return (EGLConfig)env->GetIntField(o, gConfig_EGLConfigFieldID); +} +static void nativeClassInit(JNIEnv *_env, jclass eglImplClass) +{ + gDisplay_class = make_globalref(_env, "com/google/android/gles_jni/EGLDisplayImpl"); + gContext_class = make_globalref(_env, "com/google/android/gles_jni/EGLContextImpl"); + gSurface_class = make_globalref(_env, "com/google/android/gles_jni/EGLSurfaceImpl"); + gConfig_class = make_globalref(_env, "com/google/android/gles_jni/EGLConfigImpl"); + + gConfig_ctorID = _env->GetMethodID(gConfig_class, "<init>", "(I)V"); + + gDisplay_EGLDisplayFieldID = _env->GetFieldID(gDisplay_class, "mEGLDisplay", "I"); + gContext_EGLContextFieldID = _env->GetFieldID(gContext_class, "mEGLContext", "I"); + gSurface_EGLSurfaceFieldID = _env->GetFieldID(gSurface_class, "mEGLSurface", "I"); + gSurface_NativePixelRefFieldID = _env->GetFieldID(gSurface_class, "mNativePixelRef", "I"); + gConfig_EGLConfigFieldID = _env->GetFieldID(gConfig_class, "mEGLConfig", "I"); + + jclass surface_class = _env->FindClass("android/view/Surface"); + gSurface_SurfaceFieldID = _env->GetFieldID(surface_class, "mSurface", "I"); + + jclass bitmap_class = _env->FindClass("android/graphics/Bitmap"); + gBitmap_NativeBitmapFieldID = _env->GetFieldID(bitmap_class, "mNativeBitmap", "I"); +} + +jboolean jni_eglInitialize(JNIEnv *_env, jobject _this, jobject display, + jintArray major_minor) { + + EGLDisplay dpy = getDisplay(_env, display); + jboolean success = eglInitialize(dpy, NULL, NULL); + if (success && major_minor) { + int len = _env->GetArrayLength(major_minor); + if (len) { + // we're exposing only EGL 1.0 + jint* base = (jint *)_env->GetPrimitiveArrayCritical(major_minor, (jboolean *)0); + if (len >= 1) base[0] = 1; + if (len >= 2) base[1] = 0; + _env->ReleasePrimitiveArrayCritical(major_minor, base, JNI_ABORT); + } + } + return success; +} + +jboolean jni_eglQueryContext(JNIEnv *_env, jobject _this, jobject display, + jobject context, jint attribute, jintArray value) { + EGLDisplay dpy = getDisplay(_env, display); + EGLContext ctx = getContext(_env, context); + if (value == NULL) { + doThrow(_env, "java/lang/NullPointerException"); + return JNI_FALSE; + } + jboolean success = JNI_FALSE; + int len = _env->GetArrayLength(value); + if (len) { + jint* base = (jint *)_env->GetPrimitiveArrayCritical(value, (jboolean *)0); + success = eglQueryContext(dpy, ctx, attribute, base); + _env->ReleasePrimitiveArrayCritical(value, base, JNI_ABORT); + } + return success; +} + +jboolean jni_eglQuerySurface(JNIEnv *_env, jobject _this, jobject display, + jobject surface, jint attribute, jintArray value) { + EGLDisplay dpy = getDisplay(_env, display); + EGLContext sur = getSurface(_env, surface); + if (value == NULL) { + doThrow(_env, "java/lang/NullPointerException"); + return JNI_FALSE; + } + jboolean success = JNI_FALSE; + int len = _env->GetArrayLength(value); + if (len) { + jint* base = (jint *)_env->GetPrimitiveArrayCritical(value, (jboolean *)0); + success = eglQuerySurface(dpy, sur, attribute, base); + _env->ReleasePrimitiveArrayCritical(value, base, JNI_ABORT); + } + return success; +} + +jboolean jni_eglChooseConfig(JNIEnv *_env, jobject _this, jobject display, + jintArray attrib_list, jobjectArray configs, jint config_size, jintArray num_config) { + EGLDisplay dpy = getDisplay(_env, display); + if (attrib_list==NULL || configs==NULL || num_config==NULL) { + doThrow(_env, "java/lang/NullPointerException"); + return JNI_FALSE; + } + jboolean success = JNI_FALSE; + jint* attrib_base = (jint *)_env->GetPrimitiveArrayCritical(attrib_list, (jboolean *)0); + jint* num_base = (jint *)_env->GetPrimitiveArrayCritical(num_config, (jboolean *)0); + EGLConfig nativeConfigs[config_size]; + success = eglChooseConfig(dpy, attrib_base, nativeConfigs, config_size, num_base); + int num = num_base[0]; + _env->ReleasePrimitiveArrayCritical(num_config, num_base, JNI_ABORT); + _env->ReleasePrimitiveArrayCritical(attrib_list, attrib_base, JNI_ABORT); + if (success) { + for (int i=0 ; i<num ; i++) { + jobject obj = _env->NewObject(gConfig_class, gConfig_ctorID, (jint)nativeConfigs[i]); + _env->SetObjectArrayElement(configs, i, obj); + } + } + return success; +} + +jint jni_eglCreateContext(JNIEnv *_env, jobject _this, jobject display, + jobject config, jobject share_context, jintArray attrib_list) { + EGLDisplay dpy = getDisplay(_env, display); + EGLConfig cnf = getConfig(_env, config); + EGLContext shr = getContext(_env, share_context); + jint* base = 0; + if (attrib_list) { + // XXX: if array is malformed, we should return an NPE instead of segfault + base = (jint *)_env->GetPrimitiveArrayCritical(attrib_list, (jboolean *)0); + } + EGLContext ctx = eglCreateContext(dpy, cnf, shr, base); + if (attrib_list) { + _env->ReleasePrimitiveArrayCritical(attrib_list, base, JNI_ABORT); + } + return (jint)ctx; +} + +jint jni_eglCreatePbufferSurface(JNIEnv *_env, jobject _this, jobject display, + jobject config, jintArray attrib_list) { + EGLDisplay dpy = getDisplay(_env, display); + EGLConfig cnf = getConfig(_env, config); + jint* base = 0; + if (attrib_list) { + // XXX: if array is malformed, we should return an NPE instead of segfault + base = (jint *)_env->GetPrimitiveArrayCritical(attrib_list, (jboolean *)0); + } + EGLSurface sur = eglCreatePbufferSurface(dpy, cnf, base); + if (attrib_list) { + _env->ReleasePrimitiveArrayCritical(attrib_list, base, JNI_ABORT); + } + return (jint)sur; +} + +static PixelFormat convertPixelFormat(SkBitmap::Config format) +{ + switch (format) { + case SkBitmap::kARGB_8888_Config: return PIXEL_FORMAT_RGBA_8888; + case SkBitmap::kARGB_4444_Config: return PIXEL_FORMAT_RGBA_4444; + case SkBitmap::kRGB_565_Config: return PIXEL_FORMAT_RGB_565; + case SkBitmap::kA8_Config: return PIXEL_FORMAT_A_8; + default: return PIXEL_FORMAT_NONE; + } +} + +void jni_eglCreatePixmapSurface(JNIEnv *_env, jobject _this, jobject out_sur, + jobject display, jobject config, jobject native_pixmap, + jintArray attrib_list) +{ + EGLDisplay dpy = getDisplay(_env, display); + EGLConfig cnf = getConfig(_env, config); + jint* base = 0; + + SkBitmap const * nativeBitmap = + (SkBitmap const *)_env->GetIntField(native_pixmap, + gBitmap_NativeBitmapFieldID); + SkPixelRef* ref = nativeBitmap ? nativeBitmap->pixelRef() : 0; + if (ref == NULL) { + doThrow(_env, "java/lang/NullPointerException", "Bitmap has no PixelRef"); + return; + } + + ref->safeRef(); + ref->lockPixels(); + + egl_native_pixmap_t pixmap; + pixmap.version = sizeof(pixmap); + pixmap.width = nativeBitmap->width(); + pixmap.height = nativeBitmap->height(); + pixmap.stride = nativeBitmap->rowBytes() / nativeBitmap->bytesPerPixel(); + pixmap.format = convertPixelFormat(nativeBitmap->config()); + pixmap.data = (uint8_t*)ref->pixels(); + + if (attrib_list) { + // XXX: if array is malformed, we should return an NPE instead of segfault + base = (jint *)_env->GetPrimitiveArrayCritical(attrib_list, (jboolean *)0); + } + EGLSurface sur = eglCreatePixmapSurface(dpy, cnf, &pixmap, base); + if (attrib_list) { + _env->ReleasePrimitiveArrayCritical(attrib_list, base, JNI_ABORT); + } + + if (sur != EGL_NO_SURFACE) { + _env->SetIntField(out_sur, gSurface_EGLSurfaceFieldID, (int)sur); + _env->SetIntField(out_sur, gSurface_NativePixelRefFieldID, (int)ref); + } else { + ref->unlockPixels(); + ref->safeUnref(); + } +} + +jint jni_eglCreateWindowSurface(JNIEnv *_env, jobject _this, jobject display, + jobject config, jobject native_window, jintArray attrib_list) { + EGLDisplay dpy = getDisplay(_env, display); + EGLContext cnf = getConfig(_env, config); + Surface* window = 0; + if (native_window == NULL) { +not_valid_surface: + doThrow(_env, "java/lang/NullPointerException", + "Make sure the SurfaceView or associated SurfaceHolder has a valid Surface"); + return 0; + } + window = (Surface*)_env->GetIntField(native_window, gSurface_SurfaceFieldID); + if (window == NULL) + goto not_valid_surface; + + jint* base = 0; + if (attrib_list) { + // XXX: if array is malformed, we should return an NPE instead of segfault + base = (jint *)_env->GetPrimitiveArrayCritical(attrib_list, (jboolean *)0); + } + EGLSurface sur = eglCreateWindowSurface(dpy, cnf, new EGLNativeWindowSurface(window), base); + if (attrib_list) { + _env->ReleasePrimitiveArrayCritical(attrib_list, base, JNI_ABORT); + } + return (jint)sur; +} + +jboolean jni_eglGetConfigAttrib(JNIEnv *_env, jobject _this, jobject display, + jobject config, jint attribute, jintArray value) { + EGLDisplay dpy = getDisplay(_env, display); + EGLContext cnf = getConfig(_env, config); + if (value == NULL) { + doThrow(_env, "java/lang/NullPointerException"); + return JNI_FALSE; + } + jboolean success = JNI_FALSE; + int len = _env->GetArrayLength(value); + if (len) { + jint* base = (jint *)_env->GetPrimitiveArrayCritical(value, (jboolean *)0); + success = eglGetConfigAttrib(dpy, cnf, attribute, base); + _env->ReleasePrimitiveArrayCritical(value, base, JNI_ABORT); + } + return success; +} + +jboolean jni_eglGetConfigs(JNIEnv *_env, jobject _this, jobject display, + jobjectArray configs, jint config_size, jintArray num_config) { + EGLDisplay dpy = getDisplay(_env, display); + jboolean success = JNI_FALSE; + if (num_config == NULL) { + doThrow(_env, "java/lang/NullPointerException"); + return JNI_FALSE; + } + jint* num_base = (jint *)_env->GetPrimitiveArrayCritical(num_config, (jboolean *)0); + EGLConfig nativeConfigs[config_size]; + success = eglGetConfigs(dpy, configs ? nativeConfigs : 0, config_size, num_base); + int num = num_base[0]; + _env->ReleasePrimitiveArrayCritical(num_config, num_base, JNI_ABORT); + + 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]); + } + } + return success; +} + +jint jni_eglGetError(JNIEnv *_env, jobject _this) { + EGLint error = eglGetError(); + return error; +} + +jint jni_eglGetCurrentContext(JNIEnv *_env, jobject _this) { + return (jint)eglGetCurrentContext(); +} + +jint jni_eglGetCurrentDisplay(JNIEnv *_env, jobject _this) { + return (jint)eglGetCurrentDisplay(); +} + +jint jni_eglGetCurrentSurface(JNIEnv *_env, jobject _this, jint readdraw) { + return (jint)eglGetCurrentSurface(readdraw); +} + +jboolean jni_eglDestroyContext(JNIEnv *_env, jobject _this, jobject display, jobject context) { + EGLDisplay dpy = getDisplay(_env, display); + EGLContext ctx = getContext(_env, context); + return eglDestroyContext(dpy, ctx); +} + +jboolean jni_eglDestroySurface(JNIEnv *_env, jobject _this, jobject display, jobject surface) { + EGLDisplay dpy = getDisplay(_env, display); + EGLSurface sur = getSurface(_env, surface); + + if (sur) { + SkPixelRef* ref = (SkPixelRef*)(_env->GetIntField(surface, + gSurface_NativePixelRefFieldID)); + if (ref) { + ref->unlockPixels(); + ref->safeUnref(); + } + } + return eglDestroySurface(dpy, sur); +} + +jint jni_eglGetDisplay(JNIEnv *_env, jobject _this, jobject native_display) { + return (jint)eglGetDisplay(EGL_DEFAULT_DISPLAY); +} + +jboolean jni_eglMakeCurrent(JNIEnv *_env, jobject _this, jobject display, jobject draw, jobject read, jobject context) { + EGLDisplay dpy = getDisplay(_env, display); + EGLSurface sdr = getSurface(_env, draw); + EGLSurface srd = getSurface(_env, read); + EGLContext ctx = getContext(_env, context); + return eglMakeCurrent(dpy, sdr, srd, ctx); +} + +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)); +} + +jboolean jni_eglSwapBuffers(JNIEnv *_env, jobject _this, jobject display, jobject surface) { + EGLDisplay dpy = getDisplay(_env, display); + EGLSurface sur = getSurface(_env, surface); + return eglSwapBuffers(dpy, sur); +} + +jboolean jni_eglTerminate(JNIEnv *_env, jobject _this, jobject display) { + EGLDisplay dpy = getDisplay(_env, display); + return eglTerminate(dpy); +} + +jboolean jni_eglCopyBuffers(JNIEnv *_env, jobject _this, jobject display, + jobject surface, jobject native_pixmap) { + // TODO: implement me + return JNI_FALSE; +} + +jboolean jni_eglWaitGL(JNIEnv *_env, jobject _this) { + return eglWaitGL(); +} + +jboolean jni_eglWaitNative(JNIEnv *_env, jobject _this, jint engine, jobject bindTarget) { + return eglWaitNative(engine); +} + + +static const char *classPathName = "com/google/android/gles_jni/EGLImpl"; + +#define DISPLAY "Ljavax/microedition/khronos/egl/EGLDisplay;" +#define CONTEXT "Ljavax/microedition/khronos/egl/EGLContext;" +#define CONFIG "Ljavax/microedition/khronos/egl/EGLConfig;" +#define SURFACE "Ljavax/microedition/khronos/egl/EGLSurface;" +#define OBJECT "Ljava/lang/Object;" +#define STRING "Ljava/lang/String;" + +static JNINativeMethod methods[] = { +{"_nativeClassInit","()V", (void*)nativeClassInit }, +{"eglWaitGL", "()Z", (void*)jni_eglWaitGL }, +{"eglInitialize", "(" DISPLAY "[I)Z", (void*)jni_eglInitialize }, +{"eglQueryContext", "(" DISPLAY CONTEXT "I[I)Z", (void*)jni_eglQueryContext }, +{"eglQuerySurface", "(" DISPLAY SURFACE "I[I)Z", (void*)jni_eglQuerySurface }, +{"eglChooseConfig", "(" DISPLAY "[I[" CONFIG "I[I)Z", (void*)jni_eglChooseConfig }, +{"_eglCreateContext","(" DISPLAY CONFIG CONTEXT "[I)I", (void*)jni_eglCreateContext }, +{"eglGetConfigs", "(" DISPLAY "[" CONFIG "I[I)Z", (void*)jni_eglGetConfigs }, +{"eglTerminate", "(" DISPLAY ")Z", (void*)jni_eglTerminate }, +{"eglCopyBuffers", "(" DISPLAY SURFACE OBJECT ")Z", (void*)jni_eglCopyBuffers }, +{"eglWaitNative", "(I" OBJECT ")Z", (void*)jni_eglWaitNative }, +{"eglGetError", "()I", (void*)jni_eglGetError }, +{"eglGetConfigAttrib", "(" DISPLAY CONFIG "I[I)Z", (void*)jni_eglGetConfigAttrib }, +{"_eglGetDisplay", "(" OBJECT ")I", (void*)jni_eglGetDisplay }, +{"_eglGetCurrentContext", "()I", (void*)jni_eglGetCurrentContext }, +{"_eglGetCurrentDisplay", "()I", (void*)jni_eglGetCurrentDisplay }, +{"_eglGetCurrentSurface", "(I)I", (void*)jni_eglGetCurrentSurface }, +{"_eglCreatePbufferSurface","(" DISPLAY CONFIG "[I)I", (void*)jni_eglCreatePbufferSurface }, +{"_eglCreatePixmapSurface", "(" SURFACE DISPLAY CONFIG OBJECT "[I)V", (void*)jni_eglCreatePixmapSurface }, +{"_eglCreateWindowSurface", "(" DISPLAY CONFIG OBJECT "[I)I", (void*)jni_eglCreateWindowSurface }, +{"eglDestroyContext", "(" DISPLAY CONTEXT ")Z", (void*)jni_eglDestroyContext }, +{"eglDestroySurface", "(" DISPLAY SURFACE ")Z", (void*)jni_eglDestroySurface }, +{"eglMakeCurrent", "(" DISPLAY SURFACE SURFACE CONTEXT")Z", (void*)jni_eglMakeCurrent }, +{"eglQueryString", "(" DISPLAY "I)" STRING, (void*)jni_eglQueryString }, +{"eglSwapBuffers", "(" DISPLAY SURFACE ")Z", (void*)jni_eglSwapBuffers }, +}; + +} // namespace android + +int register_com_google_android_gles_jni_EGLImpl(JNIEnv *_env) +{ + int err; + err = android::AndroidRuntime::registerNativeMethods(_env, + android::classPathName, android::methods, NELEM(android::methods)); + return err; +} + diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp new file mode 100644 index 0000000..1cd23b0 --- /dev/null +++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp @@ -0,0 +1,6571 @@ +/* //device/libs/android_runtime/com_google_android_gles_jni_GLImpl.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// This source file is automatically generated + +#include <android_runtime/AndroidRuntime.h> +#include <utils/misc.h> + +#include <assert.h> +#include <GLES/gl.h> + +#include <private/opengles/gl_context.h> + +#define _NUM_COMPRESSED_TEXTURE_FORMATS \ + (::android::OGLES_NUM_COMPRESSED_TEXTURE_FORMATS) + +static int initialized = 0; + +static jclass nioAccessClass; +static jclass bufferClass; +static jclass OOMEClass; +static jclass UOEClass; +static jclass IAEClass; +static jmethodID getBasePointerID; +static jmethodID getBaseArrayID; +static jmethodID getBaseArrayOffsetID; +static jfieldID positionID; +static jfieldID limitID; +static jfieldID elementSizeShiftID; + +/* Cache method IDs each time the class is loaded. */ + +void +nativeClassInitBuffer(JNIEnv *_env) +{ + jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess"); + nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal); + + jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); + bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); + + getBasePointerID = _env->GetStaticMethodID(nioAccessClass, + "getBasePointer", "(Ljava/nio/Buffer;)J"); + getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, + "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); + getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, + "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); + + positionID = _env->GetFieldID(bufferClass, "position", "I"); + limitID = _env->GetFieldID(bufferClass, "limit", "I"); + elementSizeShiftID = + _env->GetFieldID(bufferClass, "_elementSizeShift", "I"); +} + + +static void +nativeClassInit(JNIEnv *_env, jclass glImplClass) +{ + nativeClassInitBuffer(_env); + + jclass IAEClassLocal = + _env->FindClass("java/lang/IllegalArgumentException"); + jclass OOMEClassLocal = + _env->FindClass("java/lang/OutOfMemoryError"); + jclass UOEClassLocal = + _env->FindClass("java/lang/UnsupportedOperationException"); + + IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal); + OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal); + UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal); +} + +static void * +getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining) +{ + jint position; + jint limit; + jint elementSizeShift; + jlong pointer; + jint offset; + void *data; + + position = _env->GetIntField(buffer, positionID); + limit = _env->GetIntField(buffer, limitID); + elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); + *remaining = (limit - position) << elementSizeShift; + pointer = _env->CallStaticLongMethod(nioAccessClass, + getBasePointerID, buffer); + if (pointer != 0L) { + *array = NULL; + return (void *) (jint) pointer; + } + + *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, + getBaseArrayID, buffer); + offset = _env->CallStaticIntMethod(nioAccessClass, + getBaseArrayOffsetID, buffer); + data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0); + + return (void *) ((char *) data + offset); +} + + +static void +releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) +{ + _env->ReleasePrimitiveArrayCritical(array, data, + commit ? 0 : JNI_ABORT); +} + +// -------------------------------------------------------------------------- + +/* void glActiveTexture ( GLenum texture ) */ +static void +android_glActiveTexture__I + (JNIEnv *_env, jobject _this, jint texture) { + glActiveTexture( + (GLenum)texture + ); +} + +/* void glAlphaFunc ( GLenum func, GLclampf ref ) */ +static void +android_glAlphaFunc__IF + (JNIEnv *_env, jobject _this, jint func, jfloat ref) { + glAlphaFunc( + (GLenum)func, + (GLclampf)ref + ); +} + +/* void glAlphaFuncx ( GLenum func, GLclampx ref ) */ +static void +android_glAlphaFuncx__II + (JNIEnv *_env, jobject _this, jint func, jint ref) { + glAlphaFuncx( + (GLenum)func, + (GLclampx)ref + ); +} + +/* void glBindTexture ( GLenum target, GLuint texture ) */ +static void +android_glBindTexture__II + (JNIEnv *_env, jobject _this, jint target, jint texture) { + glBindTexture( + (GLenum)target, + (GLuint)texture + ); +} + +/* void glBlendFunc ( GLenum sfactor, GLenum dfactor ) */ +static void +android_glBlendFunc__II + (JNIEnv *_env, jobject _this, jint sfactor, jint dfactor) { + glBlendFunc( + (GLenum)sfactor, + (GLenum)dfactor + ); +} + +/* void glClear ( GLbitfield mask ) */ +static void +android_glClear__I + (JNIEnv *_env, jobject _this, jint mask) { + glClear( + (GLbitfield)mask + ); +} + +/* void glClearColor ( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha ) */ +static void +android_glClearColor__FFFF + (JNIEnv *_env, jobject _this, jfloat red, jfloat green, jfloat blue, jfloat alpha) { + glClearColor( + (GLclampf)red, + (GLclampf)green, + (GLclampf)blue, + (GLclampf)alpha + ); +} + +/* void glClearColorx ( GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha ) */ +static void +android_glClearColorx__IIII + (JNIEnv *_env, jobject _this, jint red, jint green, jint blue, jint alpha) { + glClearColorx( + (GLclampx)red, + (GLclampx)green, + (GLclampx)blue, + (GLclampx)alpha + ); +} + +/* void glClearDepthf ( GLclampf depth ) */ +static void +android_glClearDepthf__F + (JNIEnv *_env, jobject _this, jfloat depth) { + glClearDepthf( + (GLclampf)depth + ); +} + +/* void glClearDepthx ( GLclampx depth ) */ +static void +android_glClearDepthx__I + (JNIEnv *_env, jobject _this, jint depth) { + glClearDepthx( + (GLclampx)depth + ); +} + +/* void glClearStencil ( GLint s ) */ +static void +android_glClearStencil__I + (JNIEnv *_env, jobject _this, jint s) { + glClearStencil( + (GLint)s + ); +} + +/* void glClientActiveTexture ( GLenum texture ) */ +static void +android_glClientActiveTexture__I + (JNIEnv *_env, jobject _this, jint texture) { + glClientActiveTexture( + (GLenum)texture + ); +} + +/* void glColor4f ( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha ) */ +static void +android_glColor4f__FFFF + (JNIEnv *_env, jobject _this, jfloat red, jfloat green, jfloat blue, jfloat alpha) { + glColor4f( + (GLfloat)red, + (GLfloat)green, + (GLfloat)blue, + (GLfloat)alpha + ); +} + +/* void glColor4x ( GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha ) */ +static void +android_glColor4x__IIII + (JNIEnv *_env, jobject _this, jint red, jint green, jint blue, jint alpha) { + glColor4x( + (GLfixed)red, + (GLfixed)green, + (GLfixed)blue, + (GLfixed)alpha + ); +} + +/* void glColorMask ( GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha ) */ +static void +android_glColorMask__ZZZZ + (JNIEnv *_env, jobject _this, jboolean red, jboolean green, jboolean blue, jboolean alpha) { + glColorMask( + (GLboolean)red, + (GLboolean)green, + (GLboolean)blue, + (GLboolean)alpha + ); +} + +/* void glColorPointer ( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer ) */ +static void +android_glColorPointerBounds__IIILjava_nio_Buffer_2I + (JNIEnv *_env, jobject _this, jint size, jint type, jint stride, jobject pointer_buf, jint remaining) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *pointer = (GLvoid *) 0; + + pointer = (GLvoid *)getPointer(_env, pointer_buf, &_array, &_remaining); + glColorPointerBounds( + (GLint)size, + (GLenum)type, + (GLsizei)stride, + (GLvoid *)pointer, + (GLsizei)remaining + ); + if (_array) { + releasePointer(_env, _array, pointer, JNI_FALSE); + } +} + +/* void glCompressedTexImage2D ( GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data ) */ +static void +android_glCompressedTexImage2D__IIIIIIILjava_nio_Buffer_2 + (JNIEnv *_env, jobject _this, jint target, jint level, jint internalformat, jint width, jint height, jint border, jint imageSize, jobject data_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *data = (GLvoid *) 0; + + data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining); + glCompressedTexImage2D( + (GLenum)target, + (GLint)level, + (GLenum)internalformat, + (GLsizei)width, + (GLsizei)height, + (GLint)border, + (GLsizei)imageSize, + (GLvoid *)data + ); + if (_array) { + releasePointer(_env, _array, data, JNI_FALSE); + } +} + +/* void glCompressedTexSubImage2D ( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data ) */ +static void +android_glCompressedTexSubImage2D__IIIIIIIILjava_nio_Buffer_2 + (JNIEnv *_env, jobject _this, jint target, jint level, jint xoffset, jint yoffset, jint width, jint height, jint format, jint imageSize, jobject data_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *data = (GLvoid *) 0; + + data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining); + glCompressedTexSubImage2D( + (GLenum)target, + (GLint)level, + (GLint)xoffset, + (GLint)yoffset, + (GLsizei)width, + (GLsizei)height, + (GLenum)format, + (GLsizei)imageSize, + (GLvoid *)data + ); + if (_array) { + releasePointer(_env, _array, data, JNI_FALSE); + } +} + +/* void glCopyTexImage2D ( GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border ) */ +static void +android_glCopyTexImage2D__IIIIIIII + (JNIEnv *_env, jobject _this, jint target, jint level, jint internalformat, jint x, jint y, jint width, jint height, jint border) { + glCopyTexImage2D( + (GLenum)target, + (GLint)level, + (GLenum)internalformat, + (GLint)x, + (GLint)y, + (GLsizei)width, + (GLsizei)height, + (GLint)border + ); +} + +/* void glCopyTexSubImage2D ( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height ) */ +static void +android_glCopyTexSubImage2D__IIIIIIII + (JNIEnv *_env, jobject _this, jint target, jint level, jint xoffset, jint yoffset, jint x, jint y, jint width, jint height) { + glCopyTexSubImage2D( + (GLenum)target, + (GLint)level, + (GLint)xoffset, + (GLint)yoffset, + (GLint)x, + (GLint)y, + (GLsizei)width, + (GLsizei)height + ); +} + +/* void glCullFace ( GLenum mode ) */ +static void +android_glCullFace__I + (JNIEnv *_env, jobject _this, jint mode) { + glCullFace( + (GLenum)mode + ); +} + +/* void glDeleteTextures ( GLsizei n, const GLuint *textures ) */ +static void +android_glDeleteTextures__I_3II + (JNIEnv *_env, jobject _this, jint n, jintArray textures_ref, jint offset) { + GLuint *textures_base = (GLuint *) 0; + jint _remaining; + GLuint *textures = (GLuint *) 0; + + if (!textures_ref) { + _env->ThrowNew(IAEClass, "textures == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(textures_ref) - offset; + if (_remaining < n) { + _env->ThrowNew(IAEClass, "length - offset < n"); + goto exit; + } + textures_base = (GLuint *) + _env->GetPrimitiveArrayCritical(textures_ref, (jboolean *)0); + textures = textures_base + offset; + + glDeleteTextures( + (GLsizei)n, + (GLuint *)textures + ); + +exit: + if (textures_base) { + _env->ReleasePrimitiveArrayCritical(textures_ref, textures_base, + JNI_ABORT); + } +} + +/* void glDeleteTextures ( GLsizei n, const GLuint *textures ) */ +static void +android_glDeleteTextures__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint n, jobject textures_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLuint *textures = (GLuint *) 0; + + textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining); + if (_remaining < n) { + _env->ThrowNew(IAEClass, "remaining() < n"); + goto exit; + } + glDeleteTextures( + (GLsizei)n, + (GLuint *)textures + ); + +exit: + if (_array) { + releasePointer(_env, _array, textures, JNI_FALSE); + } +} + +/* void glDepthFunc ( GLenum func ) */ +static void +android_glDepthFunc__I + (JNIEnv *_env, jobject _this, jint func) { + glDepthFunc( + (GLenum)func + ); +} + +/* void glDepthMask ( GLboolean flag ) */ +static void +android_glDepthMask__Z + (JNIEnv *_env, jobject _this, jboolean flag) { + glDepthMask( + (GLboolean)flag + ); +} + +/* void glDepthRangef ( GLclampf zNear, GLclampf zFar ) */ +static void +android_glDepthRangef__FF + (JNIEnv *_env, jobject _this, jfloat zNear, jfloat zFar) { + glDepthRangef( + (GLclampf)zNear, + (GLclampf)zFar + ); +} + +/* void glDepthRangex ( GLclampx zNear, GLclampx zFar ) */ +static void +android_glDepthRangex__II + (JNIEnv *_env, jobject _this, jint zNear, jint zFar) { + glDepthRangex( + (GLclampx)zNear, + (GLclampx)zFar + ); +} + +/* void glDisable ( GLenum cap ) */ +static void +android_glDisable__I + (JNIEnv *_env, jobject _this, jint cap) { + glDisable( + (GLenum)cap + ); +} + +/* void glDisableClientState ( GLenum array ) */ +static void +android_glDisableClientState__I + (JNIEnv *_env, jobject _this, jint array) { + glDisableClientState( + (GLenum)array + ); +} + +/* void glDrawArrays ( GLenum mode, GLint first, GLsizei count ) */ +static void +android_glDrawArrays__III + (JNIEnv *_env, jobject _this, jint mode, jint first, jint count) { + glDrawArrays( + (GLenum)mode, + (GLint)first, + (GLsizei)count + ); +} + +/* void glDrawElements ( GLenum mode, GLsizei count, GLenum type, const GLvoid *indices ) */ +static void +android_glDrawElements__IIILjava_nio_Buffer_2 + (JNIEnv *_env, jobject _this, jint mode, jint count, jint type, jobject indices_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *indices = (GLvoid *) 0; + + indices = (GLvoid *)getPointer(_env, indices_buf, &_array, &_remaining); + glDrawElements( + (GLenum)mode, + (GLsizei)count, + (GLenum)type, + (GLvoid *)indices + ); + if (_array) { + releasePointer(_env, _array, indices, JNI_FALSE); + } +} + +/* void glEnable ( GLenum cap ) */ +static void +android_glEnable__I + (JNIEnv *_env, jobject _this, jint cap) { + glEnable( + (GLenum)cap + ); +} + +/* void glEnableClientState ( GLenum array ) */ +static void +android_glEnableClientState__I + (JNIEnv *_env, jobject _this, jint array) { + glEnableClientState( + (GLenum)array + ); +} + +/* void glFinish ( void ) */ +static void +android_glFinish__ + (JNIEnv *_env, jobject _this) { + glFinish(); +} + +/* void glFlush ( void ) */ +static void +android_glFlush__ + (JNIEnv *_env, jobject _this) { + glFlush(); +} + +/* void glFogf ( GLenum pname, GLfloat param ) */ +static void +android_glFogf__IF + (JNIEnv *_env, jobject _this, jint pname, jfloat param) { + glFogf( + (GLenum)pname, + (GLfloat)param + ); +} + +/* void glFogfv ( GLenum pname, const GLfloat *params ) */ +static void +android_glFogfv__I_3FI + (JNIEnv *_env, jobject _this, jint pname, jfloatArray params_ref, jint offset) { + GLfloat *params_base = (GLfloat *) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_FOG_MODE) + case GL_FOG_MODE: +#endif // defined(GL_FOG_MODE) +#if defined(GL_FOG_DENSITY) + case GL_FOG_DENSITY: +#endif // defined(GL_FOG_DENSITY) +#if defined(GL_FOG_START) + case GL_FOG_START: +#endif // defined(GL_FOG_START) +#if defined(GL_FOG_END) + case GL_FOG_END: +#endif // defined(GL_FOG_END) + _needed = 1; + break; +#if defined(GL_FOG_COLOR) + case GL_FOG_COLOR: +#endif // defined(GL_FOG_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glFogfv( + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glFogfv ( GLenum pname, const GLfloat *params ) */ +static void +android_glFogfv__ILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_FOG_MODE) + case GL_FOG_MODE: +#endif // defined(GL_FOG_MODE) +#if defined(GL_FOG_DENSITY) + case GL_FOG_DENSITY: +#endif // defined(GL_FOG_DENSITY) +#if defined(GL_FOG_START) + case GL_FOG_START: +#endif // defined(GL_FOG_START) +#if defined(GL_FOG_END) + case GL_FOG_END: +#endif // defined(GL_FOG_END) + _needed = 1; + break; +#if defined(GL_FOG_COLOR) + case GL_FOG_COLOR: +#endif // defined(GL_FOG_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glFogfv( + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glFogx ( GLenum pname, GLfixed param ) */ +static void +android_glFogx__II + (JNIEnv *_env, jobject _this, jint pname, jint param) { + glFogx( + (GLenum)pname, + (GLfixed)param + ); +} + +/* void glFogxv ( GLenum pname, const GLfixed *params ) */ +static void +android_glFogxv__I_3II + (JNIEnv *_env, jobject _this, jint pname, jintArray params_ref, jint offset) { + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_FOG_MODE) + case GL_FOG_MODE: +#endif // defined(GL_FOG_MODE) +#if defined(GL_FOG_DENSITY) + case GL_FOG_DENSITY: +#endif // defined(GL_FOG_DENSITY) +#if defined(GL_FOG_START) + case GL_FOG_START: +#endif // defined(GL_FOG_START) +#if defined(GL_FOG_END) + case GL_FOG_END: +#endif // defined(GL_FOG_END) + _needed = 1; + break; +#if defined(GL_FOG_COLOR) + case GL_FOG_COLOR: +#endif // defined(GL_FOG_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glFogxv( + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glFogxv ( GLenum pname, const GLfixed *params ) */ +static void +android_glFogxv__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_FOG_MODE) + case GL_FOG_MODE: +#endif // defined(GL_FOG_MODE) +#if defined(GL_FOG_DENSITY) + case GL_FOG_DENSITY: +#endif // defined(GL_FOG_DENSITY) +#if defined(GL_FOG_START) + case GL_FOG_START: +#endif // defined(GL_FOG_START) +#if defined(GL_FOG_END) + case GL_FOG_END: +#endif // defined(GL_FOG_END) + _needed = 1; + break; +#if defined(GL_FOG_COLOR) + case GL_FOG_COLOR: +#endif // defined(GL_FOG_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glFogxv( + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glFrontFace ( GLenum mode ) */ +static void +android_glFrontFace__I + (JNIEnv *_env, jobject _this, jint mode) { + glFrontFace( + (GLenum)mode + ); +} + +/* void glFrustumf ( GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar ) */ +static void +android_glFrustumf__FFFFFF + (JNIEnv *_env, jobject _this, jfloat left, jfloat right, jfloat bottom, jfloat top, jfloat zNear, jfloat zFar) { + glFrustumf( + (GLfloat)left, + (GLfloat)right, + (GLfloat)bottom, + (GLfloat)top, + (GLfloat)zNear, + (GLfloat)zFar + ); +} + +/* void glFrustumx ( GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar ) */ +static void +android_glFrustumx__IIIIII + (JNIEnv *_env, jobject _this, jint left, jint right, jint bottom, jint top, jint zNear, jint zFar) { + glFrustumx( + (GLfixed)left, + (GLfixed)right, + (GLfixed)bottom, + (GLfixed)top, + (GLfixed)zNear, + (GLfixed)zFar + ); +} + +/* void glGenTextures ( GLsizei n, GLuint *textures ) */ +static void +android_glGenTextures__I_3II + (JNIEnv *_env, jobject _this, jint n, jintArray textures_ref, jint offset) { + jint _exception = 0; + GLuint *textures_base = (GLuint *) 0; + jint _remaining; + GLuint *textures = (GLuint *) 0; + + if (!textures_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "textures == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(textures_ref) - offset; + if (_remaining < n) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < n"); + goto exit; + } + textures_base = (GLuint *) + _env->GetPrimitiveArrayCritical(textures_ref, (jboolean *)0); + textures = textures_base + offset; + + glGenTextures( + (GLsizei)n, + (GLuint *)textures + ); + +exit: + if (textures_base) { + _env->ReleasePrimitiveArrayCritical(textures_ref, textures_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGenTextures ( GLsizei n, GLuint *textures ) */ +static void +android_glGenTextures__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint n, jobject textures_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLuint *textures = (GLuint *) 0; + + textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining); + if (_remaining < n) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < n"); + goto exit; + } + glGenTextures( + (GLsizei)n, + (GLuint *)textures + ); + +exit: + if (_array) { + releasePointer(_env, _array, textures, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* GLenum glGetError ( void ) */ +static jint +android_glGetError__ + (JNIEnv *_env, jobject _this) { + GLenum _returnValue; + _returnValue = glGetError(); + return _returnValue; +} + +/* void glGetIntegerv ( GLenum pname, GLint *params ) */ +static void +android_glGetIntegerv__I_3II + (JNIEnv *_env, jobject _this, jint pname, jintArray params_ref, jint offset) { + jint _exception = 0; + GLint *params_base = (GLint *) 0; + jint _remaining; + GLint *params = (GLint *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_ALPHA_BITS) + case GL_ALPHA_BITS: +#endif // defined(GL_ALPHA_BITS) +#if defined(GL_ALPHA_TEST_FUNC) + case GL_ALPHA_TEST_FUNC: +#endif // defined(GL_ALPHA_TEST_FUNC) +#if defined(GL_ALPHA_TEST_REF) + case GL_ALPHA_TEST_REF: +#endif // defined(GL_ALPHA_TEST_REF) +#if defined(GL_BLEND_DST) + case GL_BLEND_DST: +#endif // defined(GL_BLEND_DST) +#if defined(GL_BLUE_BITS) + case GL_BLUE_BITS: +#endif // defined(GL_BLUE_BITS) +#if defined(GL_COLOR_ARRAY_BUFFER_BINDING) + case GL_COLOR_ARRAY_BUFFER_BINDING: +#endif // defined(GL_COLOR_ARRAY_BUFFER_BINDING) +#if defined(GL_COLOR_ARRAY_SIZE) + case GL_COLOR_ARRAY_SIZE: +#endif // defined(GL_COLOR_ARRAY_SIZE) +#if defined(GL_COLOR_ARRAY_STRIDE) + case GL_COLOR_ARRAY_STRIDE: +#endif // defined(GL_COLOR_ARRAY_STRIDE) +#if defined(GL_COLOR_ARRAY_TYPE) + case GL_COLOR_ARRAY_TYPE: +#endif // defined(GL_COLOR_ARRAY_TYPE) +#if defined(GL_CULL_FACE) + case GL_CULL_FACE: +#endif // defined(GL_CULL_FACE) +#if defined(GL_DEPTH_BITS) + case GL_DEPTH_BITS: +#endif // defined(GL_DEPTH_BITS) +#if defined(GL_DEPTH_CLEAR_VALUE) + case GL_DEPTH_CLEAR_VALUE: +#endif // defined(GL_DEPTH_CLEAR_VALUE) +#if defined(GL_DEPTH_FUNC) + case GL_DEPTH_FUNC: +#endif // defined(GL_DEPTH_FUNC) +#if defined(GL_DEPTH_WRITEMASK) + case GL_DEPTH_WRITEMASK: +#endif // defined(GL_DEPTH_WRITEMASK) +#if defined(GL_FOG_DENSITY) + case GL_FOG_DENSITY: +#endif // defined(GL_FOG_DENSITY) +#if defined(GL_FOG_END) + case GL_FOG_END: +#endif // defined(GL_FOG_END) +#if defined(GL_FOG_MODE) + case GL_FOG_MODE: +#endif // defined(GL_FOG_MODE) +#if defined(GL_FOG_START) + case GL_FOG_START: +#endif // defined(GL_FOG_START) +#if defined(GL_FRONT_FACE) + case GL_FRONT_FACE: +#endif // defined(GL_FRONT_FACE) +#if defined(GL_GREEN_BITS) + case GL_GREEN_BITS: +#endif // defined(GL_GREEN_BITS) +#if defined(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES) + case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES: +#endif // defined(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES) +#if defined(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES) + case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES: +#endif // defined(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES) +#if defined(GL_LIGHT_MODEL_TWO_SIDE) + case GL_LIGHT_MODEL_TWO_SIDE: +#endif // defined(GL_LIGHT_MODEL_TWO_SIDE) +#if defined(GL_LINE_SMOOTH_HINT) + case GL_LINE_SMOOTH_HINT: +#endif // defined(GL_LINE_SMOOTH_HINT) +#if defined(GL_LINE_WIDTH) + case GL_LINE_WIDTH: +#endif // defined(GL_LINE_WIDTH) +#if defined(GL_LOGIC_OP_MODE) + case GL_LOGIC_OP_MODE: +#endif // defined(GL_LOGIC_OP_MODE) +#if defined(GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES) + case GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES: +#endif // defined(GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES) +#if defined(GL_MATRIX_INDEX_ARRAY_SIZE_OES) + case GL_MATRIX_INDEX_ARRAY_SIZE_OES: +#endif // defined(GL_MATRIX_INDEX_ARRAY_SIZE_OES) +#if defined(GL_MATRIX_INDEX_ARRAY_STRIDE_OES) + case GL_MATRIX_INDEX_ARRAY_STRIDE_OES: +#endif // defined(GL_MATRIX_INDEX_ARRAY_STRIDE_OES) +#if defined(GL_MATRIX_INDEX_ARRAY_TYPE_OES) + case GL_MATRIX_INDEX_ARRAY_TYPE_OES: +#endif // defined(GL_MATRIX_INDEX_ARRAY_TYPE_OES) +#if defined(GL_MATRIX_MODE) + case GL_MATRIX_MODE: +#endif // defined(GL_MATRIX_MODE) +#if defined(GL_MAX_CLIP_PLANES) + case GL_MAX_CLIP_PLANES: +#endif // defined(GL_MAX_CLIP_PLANES) +#if defined(GL_MAX_ELEMENTS_INDICES) + case GL_MAX_ELEMENTS_INDICES: +#endif // defined(GL_MAX_ELEMENTS_INDICES) +#if defined(GL_MAX_ELEMENTS_VERTICES) + case GL_MAX_ELEMENTS_VERTICES: +#endif // defined(GL_MAX_ELEMENTS_VERTICES) +#if defined(GL_MAX_LIGHTS) + case GL_MAX_LIGHTS: +#endif // defined(GL_MAX_LIGHTS) +#if defined(GL_MAX_MODELVIEW_STACK_DEPTH) + case GL_MAX_MODELVIEW_STACK_DEPTH: +#endif // defined(GL_MAX_MODELVIEW_STACK_DEPTH) +#if defined(GL_MAX_PALETTE_MATRICES_OES) + case GL_MAX_PALETTE_MATRICES_OES: +#endif // defined(GL_MAX_PALETTE_MATRICES_OES) +#if defined(GL_MAX_PROJECTION_STACK_DEPTH) + case GL_MAX_PROJECTION_STACK_DEPTH: +#endif // defined(GL_MAX_PROJECTION_STACK_DEPTH) +#if defined(GL_MAX_TEXTURE_SIZE) + case GL_MAX_TEXTURE_SIZE: +#endif // defined(GL_MAX_TEXTURE_SIZE) +#if defined(GL_MAX_TEXTURE_STACK_DEPTH) + case GL_MAX_TEXTURE_STACK_DEPTH: +#endif // defined(GL_MAX_TEXTURE_STACK_DEPTH) +#if defined(GL_MAX_TEXTURE_UNITS) + case GL_MAX_TEXTURE_UNITS: +#endif // defined(GL_MAX_TEXTURE_UNITS) +#if defined(GL_MAX_VERTEX_UNITS_OES) + case GL_MAX_VERTEX_UNITS_OES: +#endif // defined(GL_MAX_VERTEX_UNITS_OES) +#if defined(GL_MODELVIEW_STACK_DEPTH) + case GL_MODELVIEW_STACK_DEPTH: +#endif // defined(GL_MODELVIEW_STACK_DEPTH) +#if defined(GL_NORMAL_ARRAY_BUFFER_BINDING) + case GL_NORMAL_ARRAY_BUFFER_BINDING: +#endif // defined(GL_NORMAL_ARRAY_BUFFER_BINDING) +#if defined(GL_NORMAL_ARRAY_STRIDE) + case GL_NORMAL_ARRAY_STRIDE: +#endif // defined(GL_NORMAL_ARRAY_STRIDE) +#if defined(GL_NORMAL_ARRAY_TYPE) + case GL_NORMAL_ARRAY_TYPE: +#endif // defined(GL_NORMAL_ARRAY_TYPE) +#if defined(GL_NUM_COMPRESSED_TEXTURE_FORMATS) + case GL_NUM_COMPRESSED_TEXTURE_FORMATS: +#endif // defined(GL_NUM_COMPRESSED_TEXTURE_FORMATS) +#if defined(GL_PACK_ALIGNMENT) + case GL_PACK_ALIGNMENT: +#endif // defined(GL_PACK_ALIGNMENT) +#if defined(GL_PERSPECTIVE_CORRECTION_HINT) + case GL_PERSPECTIVE_CORRECTION_HINT: +#endif // defined(GL_PERSPECTIVE_CORRECTION_HINT) +#if defined(GL_POINT_SIZE) + case GL_POINT_SIZE: +#endif // defined(GL_POINT_SIZE) +#if defined(GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES) + case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES: +#endif // defined(GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES) +#if defined(GL_POINT_SIZE_ARRAY_STRIDE_OES) + case GL_POINT_SIZE_ARRAY_STRIDE_OES: +#endif // defined(GL_POINT_SIZE_ARRAY_STRIDE_OES) +#if defined(GL_POINT_SIZE_ARRAY_TYPE_OES) + case GL_POINT_SIZE_ARRAY_TYPE_OES: +#endif // defined(GL_POINT_SIZE_ARRAY_TYPE_OES) +#if defined(GL_POINT_SMOOTH_HINT) + case GL_POINT_SMOOTH_HINT: +#endif // defined(GL_POINT_SMOOTH_HINT) +#if defined(GL_POLYGON_OFFSET_FACTOR) + case GL_POLYGON_OFFSET_FACTOR: +#endif // defined(GL_POLYGON_OFFSET_FACTOR) +#if defined(GL_POLYGON_OFFSET_UNITS) + case GL_POLYGON_OFFSET_UNITS: +#endif // defined(GL_POLYGON_OFFSET_UNITS) +#if defined(GL_PROJECTION_STACK_DEPTH) + case GL_PROJECTION_STACK_DEPTH: +#endif // defined(GL_PROJECTION_STACK_DEPTH) +#if defined(GL_RED_BITS) + case GL_RED_BITS: +#endif // defined(GL_RED_BITS) +#if defined(GL_SHADE_MODEL) + case GL_SHADE_MODEL: +#endif // defined(GL_SHADE_MODEL) +#if defined(GL_STENCIL_BITS) + case GL_STENCIL_BITS: +#endif // defined(GL_STENCIL_BITS) +#if defined(GL_STENCIL_CLEAR_VALUE) + case GL_STENCIL_CLEAR_VALUE: +#endif // defined(GL_STENCIL_CLEAR_VALUE) +#if defined(GL_STENCIL_FAIL) + case GL_STENCIL_FAIL: +#endif // defined(GL_STENCIL_FAIL) +#if defined(GL_STENCIL_FUNC) + case GL_STENCIL_FUNC: +#endif // defined(GL_STENCIL_FUNC) +#if defined(GL_STENCIL_PASS_DEPTH_FAIL) + case GL_STENCIL_PASS_DEPTH_FAIL: +#endif // defined(GL_STENCIL_PASS_DEPTH_FAIL) +#if defined(GL_STENCIL_PASS_DEPTH_PASS) + case GL_STENCIL_PASS_DEPTH_PASS: +#endif // defined(GL_STENCIL_PASS_DEPTH_PASS) +#if defined(GL_STENCIL_REF) + case GL_STENCIL_REF: +#endif // defined(GL_STENCIL_REF) +#if defined(GL_STENCIL_VALUE_MASK) + case GL_STENCIL_VALUE_MASK: +#endif // defined(GL_STENCIL_VALUE_MASK) +#if defined(GL_STENCIL_WRITEMASK) + case GL_STENCIL_WRITEMASK: +#endif // defined(GL_STENCIL_WRITEMASK) +#if defined(GL_SUBPIXEL_BITS) + case GL_SUBPIXEL_BITS: +#endif // defined(GL_SUBPIXEL_BITS) +#if defined(GL_TEXTURE_BINDING_2D) + case GL_TEXTURE_BINDING_2D: +#endif // defined(GL_TEXTURE_BINDING_2D) +#if defined(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING) + case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING: +#endif // defined(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING) +#if defined(GL_TEXTURE_COORD_ARRAY_SIZE) + case GL_TEXTURE_COORD_ARRAY_SIZE: +#endif // defined(GL_TEXTURE_COORD_ARRAY_SIZE) +#if defined(GL_TEXTURE_COORD_ARRAY_STRIDE) + case GL_TEXTURE_COORD_ARRAY_STRIDE: +#endif // defined(GL_TEXTURE_COORD_ARRAY_STRIDE) +#if defined(GL_TEXTURE_COORD_ARRAY_TYPE) + case GL_TEXTURE_COORD_ARRAY_TYPE: +#endif // defined(GL_TEXTURE_COORD_ARRAY_TYPE) +#if defined(GL_TEXTURE_STACK_DEPTH) + case GL_TEXTURE_STACK_DEPTH: +#endif // defined(GL_TEXTURE_STACK_DEPTH) +#if defined(GL_UNPACK_ALIGNMENT) + case GL_UNPACK_ALIGNMENT: +#endif // defined(GL_UNPACK_ALIGNMENT) +#if defined(GL_VERTEX_ARRAY_BUFFER_BINDING) + case GL_VERTEX_ARRAY_BUFFER_BINDING: +#endif // defined(GL_VERTEX_ARRAY_BUFFER_BINDING) +#if defined(GL_VERTEX_ARRAY_SIZE) + case GL_VERTEX_ARRAY_SIZE: +#endif // defined(GL_VERTEX_ARRAY_SIZE) +#if defined(GL_VERTEX_ARRAY_STRIDE) + case GL_VERTEX_ARRAY_STRIDE: +#endif // defined(GL_VERTEX_ARRAY_STRIDE) +#if defined(GL_VERTEX_ARRAY_TYPE) + case GL_VERTEX_ARRAY_TYPE: +#endif // defined(GL_VERTEX_ARRAY_TYPE) +#if defined(GL_WEIGHT_ARRAY_BUFFER_BINDING_OES) + case GL_WEIGHT_ARRAY_BUFFER_BINDING_OES: +#endif // defined(GL_WEIGHT_ARRAY_BUFFER_BINDING_OES) +#if defined(GL_WEIGHT_ARRAY_SIZE_OES) + case GL_WEIGHT_ARRAY_SIZE_OES: +#endif // defined(GL_WEIGHT_ARRAY_SIZE_OES) +#if defined(GL_WEIGHT_ARRAY_STRIDE_OES) + case GL_WEIGHT_ARRAY_STRIDE_OES: +#endif // defined(GL_WEIGHT_ARRAY_STRIDE_OES) +#if defined(GL_WEIGHT_ARRAY_TYPE_OES) + case GL_WEIGHT_ARRAY_TYPE_OES: +#endif // defined(GL_WEIGHT_ARRAY_TYPE_OES) + _needed = 1; + break; +#if defined(GL_ALIASED_POINT_SIZE_RANGE) + case GL_ALIASED_POINT_SIZE_RANGE: +#endif // defined(GL_ALIASED_POINT_SIZE_RANGE) +#if defined(GL_ALIASED_LINE_WIDTH_RANGE) + case GL_ALIASED_LINE_WIDTH_RANGE: +#endif // defined(GL_ALIASED_LINE_WIDTH_RANGE) +#if defined(GL_DEPTH_RANGE) + case GL_DEPTH_RANGE: +#endif // defined(GL_DEPTH_RANGE) +#if defined(GL_MAX_VIEWPORT_DIMS) + case GL_MAX_VIEWPORT_DIMS: +#endif // defined(GL_MAX_VIEWPORT_DIMS) +#if defined(GL_SMOOTH_LINE_WIDTH_RANGE) + case GL_SMOOTH_LINE_WIDTH_RANGE: +#endif // defined(GL_SMOOTH_LINE_WIDTH_RANGE) +#if defined(GL_SMOOTH_POINT_SIZE_RANGE) + case GL_SMOOTH_POINT_SIZE_RANGE: +#endif // defined(GL_SMOOTH_POINT_SIZE_RANGE) + _needed = 2; + break; +#if defined(GL_COLOR_CLEAR_VALUE) + case GL_COLOR_CLEAR_VALUE: +#endif // defined(GL_COLOR_CLEAR_VALUE) +#if defined(GL_COLOR_WRITEMASK) + case GL_COLOR_WRITEMASK: +#endif // defined(GL_COLOR_WRITEMASK) +#if defined(GL_SCISSOR_BOX) + case GL_SCISSOR_BOX: +#endif // defined(GL_SCISSOR_BOX) +#if defined(GL_VIEWPORT) + case GL_VIEWPORT: +#endif // defined(GL_VIEWPORT) + _needed = 4; + break; +#if defined(GL_MODELVIEW_MATRIX) + case GL_MODELVIEW_MATRIX: +#endif // defined(GL_MODELVIEW_MATRIX) +#if defined(GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES) + case GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES: +#endif // defined(GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES) +#if defined(GL_PROJECTION_MATRIX) + case GL_PROJECTION_MATRIX: +#endif // defined(GL_PROJECTION_MATRIX) +#if defined(GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES) + case GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES: +#endif // defined(GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES) +#if defined(GL_TEXTURE_MATRIX) + case GL_TEXTURE_MATRIX: +#endif // defined(GL_TEXTURE_MATRIX) +#if defined(GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES) + case GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES: +#endif // defined(GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES) + _needed = 16; + break; +#if defined(GL_COMPRESSED_TEXTURE_FORMATS) + case GL_COMPRESSED_TEXTURE_FORMATS: +#endif // defined(GL_COMPRESSED_TEXTURE_FORMATS) +#if defined(GL_FOG_COLOR) + case GL_FOG_COLOR: +#endif // defined(GL_FOG_COLOR) +#if defined(GL_LIGHT_MODEL_AMBIENT) + case GL_LIGHT_MODEL_AMBIENT: +#endif // defined(GL_LIGHT_MODEL_AMBIENT) + _needed = _NUM_COMPRESSED_TEXTURE_FORMATS; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLint *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetIntegerv( + (GLenum)pname, + (GLint *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetIntegerv ( GLenum pname, GLint *params ) */ +static void +android_glGetIntegerv__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLint *params = (GLint *) 0; + + params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_ALPHA_BITS) + case GL_ALPHA_BITS: +#endif // defined(GL_ALPHA_BITS) +#if defined(GL_ALPHA_TEST_FUNC) + case GL_ALPHA_TEST_FUNC: +#endif // defined(GL_ALPHA_TEST_FUNC) +#if defined(GL_ALPHA_TEST_REF) + case GL_ALPHA_TEST_REF: +#endif // defined(GL_ALPHA_TEST_REF) +#if defined(GL_BLEND_DST) + case GL_BLEND_DST: +#endif // defined(GL_BLEND_DST) +#if defined(GL_BLUE_BITS) + case GL_BLUE_BITS: +#endif // defined(GL_BLUE_BITS) +#if defined(GL_COLOR_ARRAY_BUFFER_BINDING) + case GL_COLOR_ARRAY_BUFFER_BINDING: +#endif // defined(GL_COLOR_ARRAY_BUFFER_BINDING) +#if defined(GL_COLOR_ARRAY_SIZE) + case GL_COLOR_ARRAY_SIZE: +#endif // defined(GL_COLOR_ARRAY_SIZE) +#if defined(GL_COLOR_ARRAY_STRIDE) + case GL_COLOR_ARRAY_STRIDE: +#endif // defined(GL_COLOR_ARRAY_STRIDE) +#if defined(GL_COLOR_ARRAY_TYPE) + case GL_COLOR_ARRAY_TYPE: +#endif // defined(GL_COLOR_ARRAY_TYPE) +#if defined(GL_CULL_FACE) + case GL_CULL_FACE: +#endif // defined(GL_CULL_FACE) +#if defined(GL_DEPTH_BITS) + case GL_DEPTH_BITS: +#endif // defined(GL_DEPTH_BITS) +#if defined(GL_DEPTH_CLEAR_VALUE) + case GL_DEPTH_CLEAR_VALUE: +#endif // defined(GL_DEPTH_CLEAR_VALUE) +#if defined(GL_DEPTH_FUNC) + case GL_DEPTH_FUNC: +#endif // defined(GL_DEPTH_FUNC) +#if defined(GL_DEPTH_WRITEMASK) + case GL_DEPTH_WRITEMASK: +#endif // defined(GL_DEPTH_WRITEMASK) +#if defined(GL_FOG_DENSITY) + case GL_FOG_DENSITY: +#endif // defined(GL_FOG_DENSITY) +#if defined(GL_FOG_END) + case GL_FOG_END: +#endif // defined(GL_FOG_END) +#if defined(GL_FOG_MODE) + case GL_FOG_MODE: +#endif // defined(GL_FOG_MODE) +#if defined(GL_FOG_START) + case GL_FOG_START: +#endif // defined(GL_FOG_START) +#if defined(GL_FRONT_FACE) + case GL_FRONT_FACE: +#endif // defined(GL_FRONT_FACE) +#if defined(GL_GREEN_BITS) + case GL_GREEN_BITS: +#endif // defined(GL_GREEN_BITS) +#if defined(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES) + case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES: +#endif // defined(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES) +#if defined(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES) + case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES: +#endif // defined(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES) +#if defined(GL_LIGHT_MODEL_TWO_SIDE) + case GL_LIGHT_MODEL_TWO_SIDE: +#endif // defined(GL_LIGHT_MODEL_TWO_SIDE) +#if defined(GL_LINE_SMOOTH_HINT) + case GL_LINE_SMOOTH_HINT: +#endif // defined(GL_LINE_SMOOTH_HINT) +#if defined(GL_LINE_WIDTH) + case GL_LINE_WIDTH: +#endif // defined(GL_LINE_WIDTH) +#if defined(GL_LOGIC_OP_MODE) + case GL_LOGIC_OP_MODE: +#endif // defined(GL_LOGIC_OP_MODE) +#if defined(GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES) + case GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES: +#endif // defined(GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES) +#if defined(GL_MATRIX_INDEX_ARRAY_SIZE_OES) + case GL_MATRIX_INDEX_ARRAY_SIZE_OES: +#endif // defined(GL_MATRIX_INDEX_ARRAY_SIZE_OES) +#if defined(GL_MATRIX_INDEX_ARRAY_STRIDE_OES) + case GL_MATRIX_INDEX_ARRAY_STRIDE_OES: +#endif // defined(GL_MATRIX_INDEX_ARRAY_STRIDE_OES) +#if defined(GL_MATRIX_INDEX_ARRAY_TYPE_OES) + case GL_MATRIX_INDEX_ARRAY_TYPE_OES: +#endif // defined(GL_MATRIX_INDEX_ARRAY_TYPE_OES) +#if defined(GL_MATRIX_MODE) + case GL_MATRIX_MODE: +#endif // defined(GL_MATRIX_MODE) +#if defined(GL_MAX_CLIP_PLANES) + case GL_MAX_CLIP_PLANES: +#endif // defined(GL_MAX_CLIP_PLANES) +#if defined(GL_MAX_ELEMENTS_INDICES) + case GL_MAX_ELEMENTS_INDICES: +#endif // defined(GL_MAX_ELEMENTS_INDICES) +#if defined(GL_MAX_ELEMENTS_VERTICES) + case GL_MAX_ELEMENTS_VERTICES: +#endif // defined(GL_MAX_ELEMENTS_VERTICES) +#if defined(GL_MAX_LIGHTS) + case GL_MAX_LIGHTS: +#endif // defined(GL_MAX_LIGHTS) +#if defined(GL_MAX_MODELVIEW_STACK_DEPTH) + case GL_MAX_MODELVIEW_STACK_DEPTH: +#endif // defined(GL_MAX_MODELVIEW_STACK_DEPTH) +#if defined(GL_MAX_PALETTE_MATRICES_OES) + case GL_MAX_PALETTE_MATRICES_OES: +#endif // defined(GL_MAX_PALETTE_MATRICES_OES) +#if defined(GL_MAX_PROJECTION_STACK_DEPTH) + case GL_MAX_PROJECTION_STACK_DEPTH: +#endif // defined(GL_MAX_PROJECTION_STACK_DEPTH) +#if defined(GL_MAX_TEXTURE_SIZE) + case GL_MAX_TEXTURE_SIZE: +#endif // defined(GL_MAX_TEXTURE_SIZE) +#if defined(GL_MAX_TEXTURE_STACK_DEPTH) + case GL_MAX_TEXTURE_STACK_DEPTH: +#endif // defined(GL_MAX_TEXTURE_STACK_DEPTH) +#if defined(GL_MAX_TEXTURE_UNITS) + case GL_MAX_TEXTURE_UNITS: +#endif // defined(GL_MAX_TEXTURE_UNITS) +#if defined(GL_MAX_VERTEX_UNITS_OES) + case GL_MAX_VERTEX_UNITS_OES: +#endif // defined(GL_MAX_VERTEX_UNITS_OES) +#if defined(GL_MODELVIEW_STACK_DEPTH) + case GL_MODELVIEW_STACK_DEPTH: +#endif // defined(GL_MODELVIEW_STACK_DEPTH) +#if defined(GL_NORMAL_ARRAY_BUFFER_BINDING) + case GL_NORMAL_ARRAY_BUFFER_BINDING: +#endif // defined(GL_NORMAL_ARRAY_BUFFER_BINDING) +#if defined(GL_NORMAL_ARRAY_STRIDE) + case GL_NORMAL_ARRAY_STRIDE: +#endif // defined(GL_NORMAL_ARRAY_STRIDE) +#if defined(GL_NORMAL_ARRAY_TYPE) + case GL_NORMAL_ARRAY_TYPE: +#endif // defined(GL_NORMAL_ARRAY_TYPE) +#if defined(GL_NUM_COMPRESSED_TEXTURE_FORMATS) + case GL_NUM_COMPRESSED_TEXTURE_FORMATS: +#endif // defined(GL_NUM_COMPRESSED_TEXTURE_FORMATS) +#if defined(GL_PACK_ALIGNMENT) + case GL_PACK_ALIGNMENT: +#endif // defined(GL_PACK_ALIGNMENT) +#if defined(GL_PERSPECTIVE_CORRECTION_HINT) + case GL_PERSPECTIVE_CORRECTION_HINT: +#endif // defined(GL_PERSPECTIVE_CORRECTION_HINT) +#if defined(GL_POINT_SIZE) + case GL_POINT_SIZE: +#endif // defined(GL_POINT_SIZE) +#if defined(GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES) + case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES: +#endif // defined(GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES) +#if defined(GL_POINT_SIZE_ARRAY_STRIDE_OES) + case GL_POINT_SIZE_ARRAY_STRIDE_OES: +#endif // defined(GL_POINT_SIZE_ARRAY_STRIDE_OES) +#if defined(GL_POINT_SIZE_ARRAY_TYPE_OES) + case GL_POINT_SIZE_ARRAY_TYPE_OES: +#endif // defined(GL_POINT_SIZE_ARRAY_TYPE_OES) +#if defined(GL_POINT_SMOOTH_HINT) + case GL_POINT_SMOOTH_HINT: +#endif // defined(GL_POINT_SMOOTH_HINT) +#if defined(GL_POLYGON_OFFSET_FACTOR) + case GL_POLYGON_OFFSET_FACTOR: +#endif // defined(GL_POLYGON_OFFSET_FACTOR) +#if defined(GL_POLYGON_OFFSET_UNITS) + case GL_POLYGON_OFFSET_UNITS: +#endif // defined(GL_POLYGON_OFFSET_UNITS) +#if defined(GL_PROJECTION_STACK_DEPTH) + case GL_PROJECTION_STACK_DEPTH: +#endif // defined(GL_PROJECTION_STACK_DEPTH) +#if defined(GL_RED_BITS) + case GL_RED_BITS: +#endif // defined(GL_RED_BITS) +#if defined(GL_SHADE_MODEL) + case GL_SHADE_MODEL: +#endif // defined(GL_SHADE_MODEL) +#if defined(GL_STENCIL_BITS) + case GL_STENCIL_BITS: +#endif // defined(GL_STENCIL_BITS) +#if defined(GL_STENCIL_CLEAR_VALUE) + case GL_STENCIL_CLEAR_VALUE: +#endif // defined(GL_STENCIL_CLEAR_VALUE) +#if defined(GL_STENCIL_FAIL) + case GL_STENCIL_FAIL: +#endif // defined(GL_STENCIL_FAIL) +#if defined(GL_STENCIL_FUNC) + case GL_STENCIL_FUNC: +#endif // defined(GL_STENCIL_FUNC) +#if defined(GL_STENCIL_PASS_DEPTH_FAIL) + case GL_STENCIL_PASS_DEPTH_FAIL: +#endif // defined(GL_STENCIL_PASS_DEPTH_FAIL) +#if defined(GL_STENCIL_PASS_DEPTH_PASS) + case GL_STENCIL_PASS_DEPTH_PASS: +#endif // defined(GL_STENCIL_PASS_DEPTH_PASS) +#if defined(GL_STENCIL_REF) + case GL_STENCIL_REF: +#endif // defined(GL_STENCIL_REF) +#if defined(GL_STENCIL_VALUE_MASK) + case GL_STENCIL_VALUE_MASK: +#endif // defined(GL_STENCIL_VALUE_MASK) +#if defined(GL_STENCIL_WRITEMASK) + case GL_STENCIL_WRITEMASK: +#endif // defined(GL_STENCIL_WRITEMASK) +#if defined(GL_SUBPIXEL_BITS) + case GL_SUBPIXEL_BITS: +#endif // defined(GL_SUBPIXEL_BITS) +#if defined(GL_TEXTURE_BINDING_2D) + case GL_TEXTURE_BINDING_2D: +#endif // defined(GL_TEXTURE_BINDING_2D) +#if defined(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING) + case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING: +#endif // defined(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING) +#if defined(GL_TEXTURE_COORD_ARRAY_SIZE) + case GL_TEXTURE_COORD_ARRAY_SIZE: +#endif // defined(GL_TEXTURE_COORD_ARRAY_SIZE) +#if defined(GL_TEXTURE_COORD_ARRAY_STRIDE) + case GL_TEXTURE_COORD_ARRAY_STRIDE: +#endif // defined(GL_TEXTURE_COORD_ARRAY_STRIDE) +#if defined(GL_TEXTURE_COORD_ARRAY_TYPE) + case GL_TEXTURE_COORD_ARRAY_TYPE: +#endif // defined(GL_TEXTURE_COORD_ARRAY_TYPE) +#if defined(GL_TEXTURE_STACK_DEPTH) + case GL_TEXTURE_STACK_DEPTH: +#endif // defined(GL_TEXTURE_STACK_DEPTH) +#if defined(GL_UNPACK_ALIGNMENT) + case GL_UNPACK_ALIGNMENT: +#endif // defined(GL_UNPACK_ALIGNMENT) +#if defined(GL_VERTEX_ARRAY_BUFFER_BINDING) + case GL_VERTEX_ARRAY_BUFFER_BINDING: +#endif // defined(GL_VERTEX_ARRAY_BUFFER_BINDING) +#if defined(GL_VERTEX_ARRAY_SIZE) + case GL_VERTEX_ARRAY_SIZE: +#endif // defined(GL_VERTEX_ARRAY_SIZE) +#if defined(GL_VERTEX_ARRAY_STRIDE) + case GL_VERTEX_ARRAY_STRIDE: +#endif // defined(GL_VERTEX_ARRAY_STRIDE) +#if defined(GL_VERTEX_ARRAY_TYPE) + case GL_VERTEX_ARRAY_TYPE: +#endif // defined(GL_VERTEX_ARRAY_TYPE) +#if defined(GL_WEIGHT_ARRAY_BUFFER_BINDING_OES) + case GL_WEIGHT_ARRAY_BUFFER_BINDING_OES: +#endif // defined(GL_WEIGHT_ARRAY_BUFFER_BINDING_OES) +#if defined(GL_WEIGHT_ARRAY_SIZE_OES) + case GL_WEIGHT_ARRAY_SIZE_OES: +#endif // defined(GL_WEIGHT_ARRAY_SIZE_OES) +#if defined(GL_WEIGHT_ARRAY_STRIDE_OES) + case GL_WEIGHT_ARRAY_STRIDE_OES: +#endif // defined(GL_WEIGHT_ARRAY_STRIDE_OES) +#if defined(GL_WEIGHT_ARRAY_TYPE_OES) + case GL_WEIGHT_ARRAY_TYPE_OES: +#endif // defined(GL_WEIGHT_ARRAY_TYPE_OES) + _needed = 1; + break; +#if defined(GL_ALIASED_POINT_SIZE_RANGE) + case GL_ALIASED_POINT_SIZE_RANGE: +#endif // defined(GL_ALIASED_POINT_SIZE_RANGE) +#if defined(GL_ALIASED_LINE_WIDTH_RANGE) + case GL_ALIASED_LINE_WIDTH_RANGE: +#endif // defined(GL_ALIASED_LINE_WIDTH_RANGE) +#if defined(GL_DEPTH_RANGE) + case GL_DEPTH_RANGE: +#endif // defined(GL_DEPTH_RANGE) +#if defined(GL_MAX_VIEWPORT_DIMS) + case GL_MAX_VIEWPORT_DIMS: +#endif // defined(GL_MAX_VIEWPORT_DIMS) +#if defined(GL_SMOOTH_LINE_WIDTH_RANGE) + case GL_SMOOTH_LINE_WIDTH_RANGE: +#endif // defined(GL_SMOOTH_LINE_WIDTH_RANGE) +#if defined(GL_SMOOTH_POINT_SIZE_RANGE) + case GL_SMOOTH_POINT_SIZE_RANGE: +#endif // defined(GL_SMOOTH_POINT_SIZE_RANGE) + _needed = 2; + break; +#if defined(GL_COLOR_CLEAR_VALUE) + case GL_COLOR_CLEAR_VALUE: +#endif // defined(GL_COLOR_CLEAR_VALUE) +#if defined(GL_COLOR_WRITEMASK) + case GL_COLOR_WRITEMASK: +#endif // defined(GL_COLOR_WRITEMASK) +#if defined(GL_SCISSOR_BOX) + case GL_SCISSOR_BOX: +#endif // defined(GL_SCISSOR_BOX) +#if defined(GL_VIEWPORT) + case GL_VIEWPORT: +#endif // defined(GL_VIEWPORT) + _needed = 4; + break; +#if defined(GL_MODELVIEW_MATRIX) + case GL_MODELVIEW_MATRIX: +#endif // defined(GL_MODELVIEW_MATRIX) +#if defined(GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES) + case GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES: +#endif // defined(GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES) +#if defined(GL_PROJECTION_MATRIX) + case GL_PROJECTION_MATRIX: +#endif // defined(GL_PROJECTION_MATRIX) +#if defined(GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES) + case GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES: +#endif // defined(GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES) +#if defined(GL_TEXTURE_MATRIX) + case GL_TEXTURE_MATRIX: +#endif // defined(GL_TEXTURE_MATRIX) +#if defined(GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES) + case GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES: +#endif // defined(GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES) + _needed = 16; + break; +#if defined(GL_COMPRESSED_TEXTURE_FORMATS) + case GL_COMPRESSED_TEXTURE_FORMATS: +#endif // defined(GL_COMPRESSED_TEXTURE_FORMATS) +#if defined(GL_FOG_COLOR) + case GL_FOG_COLOR: +#endif // defined(GL_FOG_COLOR) +#if defined(GL_LIGHT_MODEL_AMBIENT) + case GL_LIGHT_MODEL_AMBIENT: +#endif // defined(GL_LIGHT_MODEL_AMBIENT) + _needed = _NUM_COMPRESSED_TEXTURE_FORMATS; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glGetIntegerv( + (GLenum)pname, + (GLint *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +#include <string.h> + +/* const GLubyte * glGetString ( GLenum name ) */ +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); + return output; +} +/* void glHint ( GLenum target, GLenum mode ) */ +static void +android_glHint__II + (JNIEnv *_env, jobject _this, jint target, jint mode) { + glHint( + (GLenum)target, + (GLenum)mode + ); +} + +/* void glLightModelf ( GLenum pname, GLfloat param ) */ +static void +android_glLightModelf__IF + (JNIEnv *_env, jobject _this, jint pname, jfloat param) { + glLightModelf( + (GLenum)pname, + (GLfloat)param + ); +} + +/* void glLightModelfv ( GLenum pname, const GLfloat *params ) */ +static void +android_glLightModelfv__I_3FI + (JNIEnv *_env, jobject _this, jint pname, jfloatArray params_ref, jint offset) { + GLfloat *params_base = (GLfloat *) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_LIGHT_MODEL_TWO_SIDE) + case GL_LIGHT_MODEL_TWO_SIDE: +#endif // defined(GL_LIGHT_MODEL_TWO_SIDE) + _needed = 1; + break; +#if defined(GL_LIGHT_MODEL_AMBIENT) + case GL_LIGHT_MODEL_AMBIENT: +#endif // defined(GL_LIGHT_MODEL_AMBIENT) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glLightModelfv( + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glLightModelfv ( GLenum pname, const GLfloat *params ) */ +static void +android_glLightModelfv__ILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_LIGHT_MODEL_TWO_SIDE) + case GL_LIGHT_MODEL_TWO_SIDE: +#endif // defined(GL_LIGHT_MODEL_TWO_SIDE) + _needed = 1; + break; +#if defined(GL_LIGHT_MODEL_AMBIENT) + case GL_LIGHT_MODEL_AMBIENT: +#endif // defined(GL_LIGHT_MODEL_AMBIENT) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glLightModelfv( + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glLightModelx ( GLenum pname, GLfixed param ) */ +static void +android_glLightModelx__II + (JNIEnv *_env, jobject _this, jint pname, jint param) { + glLightModelx( + (GLenum)pname, + (GLfixed)param + ); +} + +/* void glLightModelxv ( GLenum pname, const GLfixed *params ) */ +static void +android_glLightModelxv__I_3II + (JNIEnv *_env, jobject _this, jint pname, jintArray params_ref, jint offset) { + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_LIGHT_MODEL_TWO_SIDE) + case GL_LIGHT_MODEL_TWO_SIDE: +#endif // defined(GL_LIGHT_MODEL_TWO_SIDE) + _needed = 1; + break; +#if defined(GL_LIGHT_MODEL_AMBIENT) + case GL_LIGHT_MODEL_AMBIENT: +#endif // defined(GL_LIGHT_MODEL_AMBIENT) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glLightModelxv( + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glLightModelxv ( GLenum pname, const GLfixed *params ) */ +static void +android_glLightModelxv__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_LIGHT_MODEL_TWO_SIDE) + case GL_LIGHT_MODEL_TWO_SIDE: +#endif // defined(GL_LIGHT_MODEL_TWO_SIDE) + _needed = 1; + break; +#if defined(GL_LIGHT_MODEL_AMBIENT) + case GL_LIGHT_MODEL_AMBIENT: +#endif // defined(GL_LIGHT_MODEL_AMBIENT) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glLightModelxv( + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glLightf ( GLenum light, GLenum pname, GLfloat param ) */ +static void +android_glLightf__IIF + (JNIEnv *_env, jobject _this, jint light, jint pname, jfloat param) { + glLightf( + (GLenum)light, + (GLenum)pname, + (GLfloat)param + ); +} + +/* void glLightfv ( GLenum light, GLenum pname, const GLfloat *params ) */ +static void +android_glLightfv__II_3FI + (JNIEnv *_env, jobject _this, jint light, jint pname, jfloatArray params_ref, jint offset) { + GLfloat *params_base = (GLfloat *) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_SPOT_EXPONENT) + case GL_SPOT_EXPONENT: +#endif // defined(GL_SPOT_EXPONENT) +#if defined(GL_SPOT_CUTOFF) + case GL_SPOT_CUTOFF: +#endif // defined(GL_SPOT_CUTOFF) +#if defined(GL_CONSTANT_ATTENUATION) + case GL_CONSTANT_ATTENUATION: +#endif // defined(GL_CONSTANT_ATTENUATION) +#if defined(GL_LINEAR_ATTENUATION) + case GL_LINEAR_ATTENUATION: +#endif // defined(GL_LINEAR_ATTENUATION) +#if defined(GL_QUADRATIC_ATTENUATION) + case GL_QUADRATIC_ATTENUATION: +#endif // defined(GL_QUADRATIC_ATTENUATION) + _needed = 1; + break; +#if defined(GL_SPOT_DIRECTION) + case GL_SPOT_DIRECTION: +#endif // defined(GL_SPOT_DIRECTION) + _needed = 3; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glLightfv( + (GLenum)light, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glLightfv ( GLenum light, GLenum pname, const GLfloat *params ) */ +static void +android_glLightfv__IILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint light, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_SPOT_EXPONENT) + case GL_SPOT_EXPONENT: +#endif // defined(GL_SPOT_EXPONENT) +#if defined(GL_SPOT_CUTOFF) + case GL_SPOT_CUTOFF: +#endif // defined(GL_SPOT_CUTOFF) +#if defined(GL_CONSTANT_ATTENUATION) + case GL_CONSTANT_ATTENUATION: +#endif // defined(GL_CONSTANT_ATTENUATION) +#if defined(GL_LINEAR_ATTENUATION) + case GL_LINEAR_ATTENUATION: +#endif // defined(GL_LINEAR_ATTENUATION) +#if defined(GL_QUADRATIC_ATTENUATION) + case GL_QUADRATIC_ATTENUATION: +#endif // defined(GL_QUADRATIC_ATTENUATION) + _needed = 1; + break; +#if defined(GL_SPOT_DIRECTION) + case GL_SPOT_DIRECTION: +#endif // defined(GL_SPOT_DIRECTION) + _needed = 3; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glLightfv( + (GLenum)light, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glLightx ( GLenum light, GLenum pname, GLfixed param ) */ +static void +android_glLightx__III + (JNIEnv *_env, jobject _this, jint light, jint pname, jint param) { + glLightx( + (GLenum)light, + (GLenum)pname, + (GLfixed)param + ); +} + +/* void glLightxv ( GLenum light, GLenum pname, const GLfixed *params ) */ +static void +android_glLightxv__II_3II + (JNIEnv *_env, jobject _this, jint light, jint pname, jintArray params_ref, jint offset) { + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_SPOT_EXPONENT) + case GL_SPOT_EXPONENT: +#endif // defined(GL_SPOT_EXPONENT) +#if defined(GL_SPOT_CUTOFF) + case GL_SPOT_CUTOFF: +#endif // defined(GL_SPOT_CUTOFF) +#if defined(GL_CONSTANT_ATTENUATION) + case GL_CONSTANT_ATTENUATION: +#endif // defined(GL_CONSTANT_ATTENUATION) +#if defined(GL_LINEAR_ATTENUATION) + case GL_LINEAR_ATTENUATION: +#endif // defined(GL_LINEAR_ATTENUATION) +#if defined(GL_QUADRATIC_ATTENUATION) + case GL_QUADRATIC_ATTENUATION: +#endif // defined(GL_QUADRATIC_ATTENUATION) + _needed = 1; + break; +#if defined(GL_SPOT_DIRECTION) + case GL_SPOT_DIRECTION: +#endif // defined(GL_SPOT_DIRECTION) + _needed = 3; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glLightxv( + (GLenum)light, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glLightxv ( GLenum light, GLenum pname, const GLfixed *params ) */ +static void +android_glLightxv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint light, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_SPOT_EXPONENT) + case GL_SPOT_EXPONENT: +#endif // defined(GL_SPOT_EXPONENT) +#if defined(GL_SPOT_CUTOFF) + case GL_SPOT_CUTOFF: +#endif // defined(GL_SPOT_CUTOFF) +#if defined(GL_CONSTANT_ATTENUATION) + case GL_CONSTANT_ATTENUATION: +#endif // defined(GL_CONSTANT_ATTENUATION) +#if defined(GL_LINEAR_ATTENUATION) + case GL_LINEAR_ATTENUATION: +#endif // defined(GL_LINEAR_ATTENUATION) +#if defined(GL_QUADRATIC_ATTENUATION) + case GL_QUADRATIC_ATTENUATION: +#endif // defined(GL_QUADRATIC_ATTENUATION) + _needed = 1; + break; +#if defined(GL_SPOT_DIRECTION) + case GL_SPOT_DIRECTION: +#endif // defined(GL_SPOT_DIRECTION) + _needed = 3; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glLightxv( + (GLenum)light, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glLineWidth ( GLfloat width ) */ +static void +android_glLineWidth__F + (JNIEnv *_env, jobject _this, jfloat width) { + glLineWidth( + (GLfloat)width + ); +} + +/* void glLineWidthx ( GLfixed width ) */ +static void +android_glLineWidthx__I + (JNIEnv *_env, jobject _this, jint width) { + glLineWidthx( + (GLfixed)width + ); +} + +/* void glLoadIdentity ( void ) */ +static void +android_glLoadIdentity__ + (JNIEnv *_env, jobject _this) { + glLoadIdentity(); +} + +/* void glLoadMatrixf ( const GLfloat *m ) */ +static void +android_glLoadMatrixf___3FI + (JNIEnv *_env, jobject _this, jfloatArray m_ref, jint offset) { + GLfloat *m_base = (GLfloat *) 0; + jint _remaining; + GLfloat *m = (GLfloat *) 0; + + if (!m_ref) { + _env->ThrowNew(IAEClass, "m == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(m_ref) - offset; + m_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(m_ref, (jboolean *)0); + m = m_base + offset; + + glLoadMatrixf( + (GLfloat *)m + ); + +exit: + if (m_base) { + _env->ReleasePrimitiveArrayCritical(m_ref, m_base, + JNI_ABORT); + } +} + +/* void glLoadMatrixf ( const GLfloat *m ) */ +static void +android_glLoadMatrixf__Ljava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jobject m_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *m = (GLfloat *) 0; + + m = (GLfloat *)getPointer(_env, m_buf, &_array, &_remaining); + glLoadMatrixf( + (GLfloat *)m + ); + if (_array) { + releasePointer(_env, _array, m, JNI_FALSE); + } +} + +/* void glLoadMatrixx ( const GLfixed *m ) */ +static void +android_glLoadMatrixx___3II + (JNIEnv *_env, jobject _this, jintArray m_ref, jint offset) { + GLfixed *m_base = (GLfixed *) 0; + jint _remaining; + GLfixed *m = (GLfixed *) 0; + + if (!m_ref) { + _env->ThrowNew(IAEClass, "m == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(m_ref) - offset; + m_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(m_ref, (jboolean *)0); + m = m_base + offset; + + glLoadMatrixx( + (GLfixed *)m + ); + +exit: + if (m_base) { + _env->ReleasePrimitiveArrayCritical(m_ref, m_base, + JNI_ABORT); + } +} + +/* void glLoadMatrixx ( const GLfixed *m ) */ +static void +android_glLoadMatrixx__Ljava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jobject m_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *m = (GLfixed *) 0; + + m = (GLfixed *)getPointer(_env, m_buf, &_array, &_remaining); + glLoadMatrixx( + (GLfixed *)m + ); + if (_array) { + releasePointer(_env, _array, m, JNI_FALSE); + } +} + +/* void glLogicOp ( GLenum opcode ) */ +static void +android_glLogicOp__I + (JNIEnv *_env, jobject _this, jint opcode) { + glLogicOp( + (GLenum)opcode + ); +} + +/* void glMaterialf ( GLenum face, GLenum pname, GLfloat param ) */ +static void +android_glMaterialf__IIF + (JNIEnv *_env, jobject _this, jint face, jint pname, jfloat param) { + glMaterialf( + (GLenum)face, + (GLenum)pname, + (GLfloat)param + ); +} + +/* void glMaterialfv ( GLenum face, GLenum pname, const GLfloat *params ) */ +static void +android_glMaterialfv__II_3FI + (JNIEnv *_env, jobject _this, jint face, jint pname, jfloatArray params_ref, jint offset) { + GLfloat *params_base = (GLfloat *) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_SHININESS) + case GL_SHININESS: +#endif // defined(GL_SHININESS) + _needed = 1; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) +#if defined(GL_AMBIENT_AND_DIFFUSE) + case GL_AMBIENT_AND_DIFFUSE: +#endif // defined(GL_AMBIENT_AND_DIFFUSE) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glMaterialfv( + (GLenum)face, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glMaterialfv ( GLenum face, GLenum pname, const GLfloat *params ) */ +static void +android_glMaterialfv__IILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint face, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_SHININESS) + case GL_SHININESS: +#endif // defined(GL_SHININESS) + _needed = 1; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) +#if defined(GL_AMBIENT_AND_DIFFUSE) + case GL_AMBIENT_AND_DIFFUSE: +#endif // defined(GL_AMBIENT_AND_DIFFUSE) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glMaterialfv( + (GLenum)face, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glMaterialx ( GLenum face, GLenum pname, GLfixed param ) */ +static void +android_glMaterialx__III + (JNIEnv *_env, jobject _this, jint face, jint pname, jint param) { + glMaterialx( + (GLenum)face, + (GLenum)pname, + (GLfixed)param + ); +} + +/* void glMaterialxv ( GLenum face, GLenum pname, const GLfixed *params ) */ +static void +android_glMaterialxv__II_3II + (JNIEnv *_env, jobject _this, jint face, jint pname, jintArray params_ref, jint offset) { + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_SHININESS) + case GL_SHININESS: +#endif // defined(GL_SHININESS) + _needed = 1; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) +#if defined(GL_AMBIENT_AND_DIFFUSE) + case GL_AMBIENT_AND_DIFFUSE: +#endif // defined(GL_AMBIENT_AND_DIFFUSE) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glMaterialxv( + (GLenum)face, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glMaterialxv ( GLenum face, GLenum pname, const GLfixed *params ) */ +static void +android_glMaterialxv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint face, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_SHININESS) + case GL_SHININESS: +#endif // defined(GL_SHININESS) + _needed = 1; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) +#if defined(GL_AMBIENT_AND_DIFFUSE) + case GL_AMBIENT_AND_DIFFUSE: +#endif // defined(GL_AMBIENT_AND_DIFFUSE) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glMaterialxv( + (GLenum)face, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glMatrixMode ( GLenum mode ) */ +static void +android_glMatrixMode__I + (JNIEnv *_env, jobject _this, jint mode) { + glMatrixMode( + (GLenum)mode + ); +} + +/* void glMultMatrixf ( const GLfloat *m ) */ +static void +android_glMultMatrixf___3FI + (JNIEnv *_env, jobject _this, jfloatArray m_ref, jint offset) { + GLfloat *m_base = (GLfloat *) 0; + jint _remaining; + GLfloat *m = (GLfloat *) 0; + + if (!m_ref) { + _env->ThrowNew(IAEClass, "m == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(m_ref) - offset; + m_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(m_ref, (jboolean *)0); + m = m_base + offset; + + glMultMatrixf( + (GLfloat *)m + ); + +exit: + if (m_base) { + _env->ReleasePrimitiveArrayCritical(m_ref, m_base, + JNI_ABORT); + } +} + +/* void glMultMatrixf ( const GLfloat *m ) */ +static void +android_glMultMatrixf__Ljava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jobject m_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *m = (GLfloat *) 0; + + m = (GLfloat *)getPointer(_env, m_buf, &_array, &_remaining); + glMultMatrixf( + (GLfloat *)m + ); + if (_array) { + releasePointer(_env, _array, m, JNI_FALSE); + } +} + +/* void glMultMatrixx ( const GLfixed *m ) */ +static void +android_glMultMatrixx___3II + (JNIEnv *_env, jobject _this, jintArray m_ref, jint offset) { + GLfixed *m_base = (GLfixed *) 0; + jint _remaining; + GLfixed *m = (GLfixed *) 0; + + if (!m_ref) { + _env->ThrowNew(IAEClass, "m == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(m_ref) - offset; + m_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(m_ref, (jboolean *)0); + m = m_base + offset; + + glMultMatrixx( + (GLfixed *)m + ); + +exit: + if (m_base) { + _env->ReleasePrimitiveArrayCritical(m_ref, m_base, + JNI_ABORT); + } +} + +/* void glMultMatrixx ( const GLfixed *m ) */ +static void +android_glMultMatrixx__Ljava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jobject m_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *m = (GLfixed *) 0; + + m = (GLfixed *)getPointer(_env, m_buf, &_array, &_remaining); + glMultMatrixx( + (GLfixed *)m + ); + if (_array) { + releasePointer(_env, _array, m, JNI_FALSE); + } +} + +/* void glMultiTexCoord4f ( GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q ) */ +static void +android_glMultiTexCoord4f__IFFFF + (JNIEnv *_env, jobject _this, jint target, jfloat s, jfloat t, jfloat r, jfloat q) { + glMultiTexCoord4f( + (GLenum)target, + (GLfloat)s, + (GLfloat)t, + (GLfloat)r, + (GLfloat)q + ); +} + +/* void glMultiTexCoord4x ( GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q ) */ +static void +android_glMultiTexCoord4x__IIIII + (JNIEnv *_env, jobject _this, jint target, jint s, jint t, jint r, jint q) { + glMultiTexCoord4x( + (GLenum)target, + (GLfixed)s, + (GLfixed)t, + (GLfixed)r, + (GLfixed)q + ); +} + +/* void glNormal3f ( GLfloat nx, GLfloat ny, GLfloat nz ) */ +static void +android_glNormal3f__FFF + (JNIEnv *_env, jobject _this, jfloat nx, jfloat ny, jfloat nz) { + glNormal3f( + (GLfloat)nx, + (GLfloat)ny, + (GLfloat)nz + ); +} + +/* void glNormal3x ( GLfixed nx, GLfixed ny, GLfixed nz ) */ +static void +android_glNormal3x__III + (JNIEnv *_env, jobject _this, jint nx, jint ny, jint nz) { + glNormal3x( + (GLfixed)nx, + (GLfixed)ny, + (GLfixed)nz + ); +} + +/* void glNormalPointer ( GLenum type, GLsizei stride, const GLvoid *pointer ) */ +static void +android_glNormalPointerBounds__IILjava_nio_Buffer_2I + (JNIEnv *_env, jobject _this, jint type, jint stride, jobject pointer_buf, jint remaining) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *pointer = (GLvoid *) 0; + + pointer = (GLvoid *)getPointer(_env, pointer_buf, &_array, &_remaining); + glNormalPointerBounds( + (GLenum)type, + (GLsizei)stride, + (GLvoid *)pointer, + (GLsizei)remaining + ); + if (_array) { + releasePointer(_env, _array, pointer, JNI_FALSE); + } +} + +/* void glOrthof ( GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar ) */ +static void +android_glOrthof__FFFFFF + (JNIEnv *_env, jobject _this, jfloat left, jfloat right, jfloat bottom, jfloat top, jfloat zNear, jfloat zFar) { + glOrthof( + (GLfloat)left, + (GLfloat)right, + (GLfloat)bottom, + (GLfloat)top, + (GLfloat)zNear, + (GLfloat)zFar + ); +} + +/* void glOrthox ( GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar ) */ +static void +android_glOrthox__IIIIII + (JNIEnv *_env, jobject _this, jint left, jint right, jint bottom, jint top, jint zNear, jint zFar) { + glOrthox( + (GLfixed)left, + (GLfixed)right, + (GLfixed)bottom, + (GLfixed)top, + (GLfixed)zNear, + (GLfixed)zFar + ); +} + +/* void glPixelStorei ( GLenum pname, GLint param ) */ +static void +android_glPixelStorei__II + (JNIEnv *_env, jobject _this, jint pname, jint param) { + glPixelStorei( + (GLenum)pname, + (GLint)param + ); +} + +/* void glPointSize ( GLfloat size ) */ +static void +android_glPointSize__F + (JNIEnv *_env, jobject _this, jfloat size) { + glPointSize( + (GLfloat)size + ); +} + +/* void glPointSizex ( GLfixed size ) */ +static void +android_glPointSizex__I + (JNIEnv *_env, jobject _this, jint size) { + glPointSizex( + (GLfixed)size + ); +} + +/* void glPolygonOffset ( GLfloat factor, GLfloat units ) */ +static void +android_glPolygonOffset__FF + (JNIEnv *_env, jobject _this, jfloat factor, jfloat units) { + glPolygonOffset( + (GLfloat)factor, + (GLfloat)units + ); +} + +/* void glPolygonOffsetx ( GLfixed factor, GLfixed units ) */ +static void +android_glPolygonOffsetx__II + (JNIEnv *_env, jobject _this, jint factor, jint units) { + glPolygonOffsetx( + (GLfixed)factor, + (GLfixed)units + ); +} + +/* void glPopMatrix ( void ) */ +static void +android_glPopMatrix__ + (JNIEnv *_env, jobject _this) { + glPopMatrix(); +} + +/* void glPushMatrix ( void ) */ +static void +android_glPushMatrix__ + (JNIEnv *_env, jobject _this) { + glPushMatrix(); +} + +/* void glReadPixels ( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels ) */ +static void +android_glReadPixels__IIIIIILjava_nio_Buffer_2 + (JNIEnv *_env, jobject _this, jint x, jint y, jint width, jint height, jint format, jint type, jobject pixels_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *pixels = (GLvoid *) 0; + + pixels = (GLvoid *)getPointer(_env, pixels_buf, &_array, &_remaining); + glReadPixels( + (GLint)x, + (GLint)y, + (GLsizei)width, + (GLsizei)height, + (GLenum)format, + (GLenum)type, + (GLvoid *)pixels + ); + if (_array) { + releasePointer(_env, _array, pixels, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glRotatef ( GLfloat angle, GLfloat x, GLfloat y, GLfloat z ) */ +static void +android_glRotatef__FFFF + (JNIEnv *_env, jobject _this, jfloat angle, jfloat x, jfloat y, jfloat z) { + glRotatef( + (GLfloat)angle, + (GLfloat)x, + (GLfloat)y, + (GLfloat)z + ); +} + +/* void glRotatex ( GLfixed angle, GLfixed x, GLfixed y, GLfixed z ) */ +static void +android_glRotatex__IIII + (JNIEnv *_env, jobject _this, jint angle, jint x, jint y, jint z) { + glRotatex( + (GLfixed)angle, + (GLfixed)x, + (GLfixed)y, + (GLfixed)z + ); +} + +/* void glSampleCoverage ( GLclampf value, GLboolean invert ) */ +static void +android_glSampleCoverage__FZ + (JNIEnv *_env, jobject _this, jfloat value, jboolean invert) { + glSampleCoverage( + (GLclampf)value, + (GLboolean)invert + ); +} + +/* void glSampleCoveragex ( GLclampx value, GLboolean invert ) */ +static void +android_glSampleCoveragex__IZ + (JNIEnv *_env, jobject _this, jint value, jboolean invert) { + glSampleCoveragex( + (GLclampx)value, + (GLboolean)invert + ); +} + +/* void glScalef ( GLfloat x, GLfloat y, GLfloat z ) */ +static void +android_glScalef__FFF + (JNIEnv *_env, jobject _this, jfloat x, jfloat y, jfloat z) { + glScalef( + (GLfloat)x, + (GLfloat)y, + (GLfloat)z + ); +} + +/* void glScalex ( GLfixed x, GLfixed y, GLfixed z ) */ +static void +android_glScalex__III + (JNIEnv *_env, jobject _this, jint x, jint y, jint z) { + glScalex( + (GLfixed)x, + (GLfixed)y, + (GLfixed)z + ); +} + +/* void glScissor ( GLint x, GLint y, GLsizei width, GLsizei height ) */ +static void +android_glScissor__IIII + (JNIEnv *_env, jobject _this, jint x, jint y, jint width, jint height) { + glScissor( + (GLint)x, + (GLint)y, + (GLsizei)width, + (GLsizei)height + ); +} + +/* void glShadeModel ( GLenum mode ) */ +static void +android_glShadeModel__I + (JNIEnv *_env, jobject _this, jint mode) { + glShadeModel( + (GLenum)mode + ); +} + +/* void glStencilFunc ( GLenum func, GLint ref, GLuint mask ) */ +static void +android_glStencilFunc__III + (JNIEnv *_env, jobject _this, jint func, jint ref, jint mask) { + glStencilFunc( + (GLenum)func, + (GLint)ref, + (GLuint)mask + ); +} + +/* void glStencilMask ( GLuint mask ) */ +static void +android_glStencilMask__I + (JNIEnv *_env, jobject _this, jint mask) { + glStencilMask( + (GLuint)mask + ); +} + +/* void glStencilOp ( GLenum fail, GLenum zfail, GLenum zpass ) */ +static void +android_glStencilOp__III + (JNIEnv *_env, jobject _this, jint fail, jint zfail, jint zpass) { + glStencilOp( + (GLenum)fail, + (GLenum)zfail, + (GLenum)zpass + ); +} + +/* void glTexCoordPointer ( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer ) */ +static void +android_glTexCoordPointerBounds__IIILjava_nio_Buffer_2I + (JNIEnv *_env, jobject _this, jint size, jint type, jint stride, jobject pointer_buf, jint remaining) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *pointer = (GLvoid *) 0; + + pointer = (GLvoid *)getPointer(_env, pointer_buf, &_array, &_remaining); + glTexCoordPointerBounds( + (GLint)size, + (GLenum)type, + (GLsizei)stride, + (GLvoid *)pointer, + (GLsizei)remaining + ); + if (_array) { + releasePointer(_env, _array, pointer, JNI_FALSE); + } +} + +/* void glTexEnvf ( GLenum target, GLenum pname, GLfloat param ) */ +static void +android_glTexEnvf__IIF + (JNIEnv *_env, jobject _this, jint target, jint pname, jfloat param) { + glTexEnvf( + (GLenum)target, + (GLenum)pname, + (GLfloat)param + ); +} + +/* void glTexEnvfv ( GLenum target, GLenum pname, const GLfloat *params ) */ +static void +android_glTexEnvfv__II_3FI + (JNIEnv *_env, jobject _this, jint target, jint pname, jfloatArray params_ref, jint offset) { + GLfloat *params_base = (GLfloat *) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_TEXTURE_ENV_MODE) + case GL_TEXTURE_ENV_MODE: +#endif // defined(GL_TEXTURE_ENV_MODE) +#if defined(GL_COMBINE_RGB) + case GL_COMBINE_RGB: +#endif // defined(GL_COMBINE_RGB) +#if defined(GL_COMBINE_ALPHA) + case GL_COMBINE_ALPHA: +#endif // defined(GL_COMBINE_ALPHA) + _needed = 1; + break; +#if defined(GL_TEXTURE_ENV_COLOR) + case GL_TEXTURE_ENV_COLOR: +#endif // defined(GL_TEXTURE_ENV_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glTexEnvfv( + (GLenum)target, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glTexEnvfv ( GLenum target, GLenum pname, const GLfloat *params ) */ +static void +android_glTexEnvfv__IILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_TEXTURE_ENV_MODE) + case GL_TEXTURE_ENV_MODE: +#endif // defined(GL_TEXTURE_ENV_MODE) +#if defined(GL_COMBINE_RGB) + case GL_COMBINE_RGB: +#endif // defined(GL_COMBINE_RGB) +#if defined(GL_COMBINE_ALPHA) + case GL_COMBINE_ALPHA: +#endif // defined(GL_COMBINE_ALPHA) + _needed = 1; + break; +#if defined(GL_TEXTURE_ENV_COLOR) + case GL_TEXTURE_ENV_COLOR: +#endif // defined(GL_TEXTURE_ENV_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glTexEnvfv( + (GLenum)target, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glTexEnvx ( GLenum target, GLenum pname, GLfixed param ) */ +static void +android_glTexEnvx__III + (JNIEnv *_env, jobject _this, jint target, jint pname, jint param) { + glTexEnvx( + (GLenum)target, + (GLenum)pname, + (GLfixed)param + ); +} + +/* void glTexEnvxv ( GLenum target, GLenum pname, const GLfixed *params ) */ +static void +android_glTexEnvxv__II_3II + (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) { + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_TEXTURE_ENV_MODE) + case GL_TEXTURE_ENV_MODE: +#endif // defined(GL_TEXTURE_ENV_MODE) +#if defined(GL_COMBINE_RGB) + case GL_COMBINE_RGB: +#endif // defined(GL_COMBINE_RGB) +#if defined(GL_COMBINE_ALPHA) + case GL_COMBINE_ALPHA: +#endif // defined(GL_COMBINE_ALPHA) + _needed = 1; + break; +#if defined(GL_TEXTURE_ENV_COLOR) + case GL_TEXTURE_ENV_COLOR: +#endif // defined(GL_TEXTURE_ENV_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glTexEnvxv( + (GLenum)target, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glTexEnvxv ( GLenum target, GLenum pname, const GLfixed *params ) */ +static void +android_glTexEnvxv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_TEXTURE_ENV_MODE) + case GL_TEXTURE_ENV_MODE: +#endif // defined(GL_TEXTURE_ENV_MODE) +#if defined(GL_COMBINE_RGB) + case GL_COMBINE_RGB: +#endif // defined(GL_COMBINE_RGB) +#if defined(GL_COMBINE_ALPHA) + case GL_COMBINE_ALPHA: +#endif // defined(GL_COMBINE_ALPHA) + _needed = 1; + break; +#if defined(GL_TEXTURE_ENV_COLOR) + case GL_TEXTURE_ENV_COLOR: +#endif // defined(GL_TEXTURE_ENV_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glTexEnvxv( + (GLenum)target, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glTexImage2D ( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels ) */ +static void +android_glTexImage2D__IIIIIIIILjava_nio_Buffer_2 + (JNIEnv *_env, jobject _this, jint target, jint level, jint internalformat, jint width, jint height, jint border, jint format, jint type, jobject pixels_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *pixels = (GLvoid *) 0; + + if (pixels_buf) { + pixels = (GLvoid *)getPointer(_env, pixels_buf, &_array, &_remaining); + } + glTexImage2D( + (GLenum)target, + (GLint)level, + (GLint)internalformat, + (GLsizei)width, + (GLsizei)height, + (GLint)border, + (GLenum)format, + (GLenum)type, + (GLvoid *)pixels + ); + if (_array) { + releasePointer(_env, _array, pixels, JNI_FALSE); + } +} + +/* void glTexParameterf ( GLenum target, GLenum pname, GLfloat param ) */ +static void +android_glTexParameterf__IIF + (JNIEnv *_env, jobject _this, jint target, jint pname, jfloat param) { + glTexParameterf( + (GLenum)target, + (GLenum)pname, + (GLfloat)param + ); +} + +/* void glTexParameterx ( GLenum target, GLenum pname, GLfixed param ) */ +static void +android_glTexParameterx__III + (JNIEnv *_env, jobject _this, jint target, jint pname, jint param) { + glTexParameterx( + (GLenum)target, + (GLenum)pname, + (GLfixed)param + ); +} + +/* void glTexSubImage2D ( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels ) */ +static void +android_glTexSubImage2D__IIIIIIIILjava_nio_Buffer_2 + (JNIEnv *_env, jobject _this, jint target, jint level, jint xoffset, jint yoffset, jint width, jint height, jint format, jint type, jobject pixels_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *pixels = (GLvoid *) 0; + + if (pixels_buf) { + pixels = (GLvoid *)getPointer(_env, pixels_buf, &_array, &_remaining); + } + glTexSubImage2D( + (GLenum)target, + (GLint)level, + (GLint)xoffset, + (GLint)yoffset, + (GLsizei)width, + (GLsizei)height, + (GLenum)format, + (GLenum)type, + (GLvoid *)pixels + ); + if (_array) { + releasePointer(_env, _array, pixels, JNI_FALSE); + } +} + +/* void glTranslatef ( GLfloat x, GLfloat y, GLfloat z ) */ +static void +android_glTranslatef__FFF + (JNIEnv *_env, jobject _this, jfloat x, jfloat y, jfloat z) { + glTranslatef( + (GLfloat)x, + (GLfloat)y, + (GLfloat)z + ); +} + +/* void glTranslatex ( GLfixed x, GLfixed y, GLfixed z ) */ +static void +android_glTranslatex__III + (JNIEnv *_env, jobject _this, jint x, jint y, jint z) { + glTranslatex( + (GLfixed)x, + (GLfixed)y, + (GLfixed)z + ); +} + +/* void glVertexPointer ( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer ) */ +static void +android_glVertexPointerBounds__IIILjava_nio_Buffer_2I + (JNIEnv *_env, jobject _this, jint size, jint type, jint stride, jobject pointer_buf, jint remaining) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *pointer = (GLvoid *) 0; + + pointer = (GLvoid *)getPointer(_env, pointer_buf, &_array, &_remaining); + glVertexPointerBounds( + (GLint)size, + (GLenum)type, + (GLsizei)stride, + (GLvoid *)pointer, + (GLsizei)remaining + ); + if (_array) { + releasePointer(_env, _array, pointer, JNI_FALSE); + } +} + +/* void glViewport ( GLint x, GLint y, GLsizei width, GLsizei height ) */ +static void +android_glViewport__IIII + (JNIEnv *_env, jobject _this, jint x, jint y, jint width, jint height) { + glViewport( + (GLint)x, + (GLint)y, + (GLsizei)width, + (GLsizei)height + ); +} + +/* GLbitfield glQueryMatrixxOES ( GLfixed *mantissa, GLint *exponent ) */ +static jint +android_glQueryMatrixxOES___3II_3II + (JNIEnv *_env, jobject _this, jintArray mantissa_ref, jint mantissaOffset, jintArray exponent_ref, jint exponentOffset) { + jint _exception = 0; + GLbitfield _returnValue = -1; + GLfixed *mantissa_base = (GLfixed *) 0; + jint _mantissaRemaining; + GLfixed *mantissa = (GLfixed *) 0; + GLint *exponent_base = (GLint *) 0; + jint _exponentRemaining; + GLint *exponent = (GLint *) 0; + + if (!mantissa_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "mantissa == null"); + goto exit; + } + if (mantissaOffset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "mantissaOffset < 0"); + goto exit; + } + _mantissaRemaining = _env->GetArrayLength(mantissa_ref) - mantissaOffset; + if (_mantissaRemaining < 16) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - mantissaOffset < 16"); + goto exit; + } + mantissa_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(mantissa_ref, (jboolean *)0); + mantissa = mantissa_base + mantissaOffset; + + if (!exponent_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "exponent == null"); + goto exit; + } + if (exponentOffset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "exponentOffset < 0"); + goto exit; + } + _exponentRemaining = _env->GetArrayLength(exponent_ref) - exponentOffset; + if (_exponentRemaining < 16) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - exponentOffset < 16"); + goto exit; + } + exponent_base = (GLint *) + _env->GetPrimitiveArrayCritical(exponent_ref, (jboolean *)0); + exponent = exponent_base + exponentOffset; + + _returnValue = glQueryMatrixxOES( + (GLfixed *)mantissa, + (GLint *)exponent + ); + +exit: + if (exponent_base) { + _env->ReleasePrimitiveArrayCritical(exponent_ref, exponent_base, + _exception ? JNI_ABORT: 0); + } + if (mantissa_base) { + _env->ReleasePrimitiveArrayCritical(mantissa_ref, mantissa_base, + _exception ? JNI_ABORT: 0); + } + return _returnValue; +} + +/* GLbitfield glQueryMatrixxOES ( GLfixed *mantissa, GLint *exponent ) */ +static jint +android_glQueryMatrixxOES__Ljava_nio_IntBuffer_2Ljava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jobject mantissa_buf, jobject exponent_buf) { + jint _exception = 0; + jarray _mantissaArray = (jarray) 0; + jarray _exponentArray = (jarray) 0; + GLbitfield _returnValue = -1; + jint _mantissaRemaining; + GLfixed *mantissa = (GLfixed *) 0; + jint _exponentRemaining; + GLint *exponent = (GLint *) 0; + + mantissa = (GLfixed *)getPointer(_env, mantissa_buf, &_mantissaArray, &_mantissaRemaining); + if (_mantissaRemaining < 16) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < 16"); + goto exit; + } + exponent = (GLint *)getPointer(_env, exponent_buf, &_exponentArray, &_exponentRemaining); + if (_exponentRemaining < 16) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < 16"); + goto exit; + } + _returnValue = glQueryMatrixxOES( + (GLfixed *)mantissa, + (GLint *)exponent + ); + +exit: + if (_mantissaArray) { + releasePointer(_env, _mantissaArray, exponent, _exception ? JNI_FALSE : JNI_TRUE); + } + if (_exponentArray) { + releasePointer(_env, _exponentArray, mantissa, _exception ? JNI_FALSE : JNI_TRUE); + } + return _returnValue; +} + +/* void glBindBuffer ( GLenum target, GLuint buffer ) */ +static void +android_glBindBuffer__II + (JNIEnv *_env, jobject _this, jint target, jint buffer) { + glBindBuffer( + (GLenum)target, + (GLuint)buffer + ); +} + +/* void glBufferData ( GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage ) */ +static void +android_glBufferData__IILjava_nio_Buffer_2I + (JNIEnv *_env, jobject _this, jint target, jint size, jobject data_buf, jint usage) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *data = (GLvoid *) 0; + + if (data_buf) { + data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining); + } + glBufferData( + (GLenum)target, + (GLsizeiptr)size, + (GLvoid *)data, + (GLenum)usage + ); + if (_array) { + releasePointer(_env, _array, data, JNI_FALSE); + } +} + +/* void glBufferSubData ( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data ) */ +static void +android_glBufferSubData__IIILjava_nio_Buffer_2 + (JNIEnv *_env, jobject _this, jint target, jint offset, jint size, jobject data_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *data = (GLvoid *) 0; + + data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining); + glBufferSubData( + (GLenum)target, + (GLintptr)offset, + (GLsizeiptr)size, + (GLvoid *)data + ); + if (_array) { + releasePointer(_env, _array, data, JNI_FALSE); + } +} + +/* void glClipPlanef ( GLenum plane, const GLfloat *equation ) */ +static void +android_glClipPlanef__I_3FI + (JNIEnv *_env, jobject _this, jint plane, jfloatArray equation_ref, jint offset) { + GLfloat *equation_base = (GLfloat *) 0; + jint _remaining; + GLfloat *equation = (GLfloat *) 0; + + if (!equation_ref) { + _env->ThrowNew(IAEClass, "equation == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(equation_ref) - offset; + if (_remaining < 4) { + _env->ThrowNew(IAEClass, "length - offset < 4"); + goto exit; + } + equation_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(equation_ref, (jboolean *)0); + equation = equation_base + offset; + + glClipPlanef( + (GLenum)plane, + (GLfloat *)equation + ); + +exit: + if (equation_base) { + _env->ReleasePrimitiveArrayCritical(equation_ref, equation_base, + JNI_ABORT); + } +} + +/* void glClipPlanef ( GLenum plane, const GLfloat *equation ) */ +static void +android_glClipPlanef__ILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint plane, jobject equation_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *equation = (GLfloat *) 0; + + equation = (GLfloat *)getPointer(_env, equation_buf, &_array, &_remaining); + if (_remaining < 4) { + _env->ThrowNew(IAEClass, "remaining() < 4"); + goto exit; + } + glClipPlanef( + (GLenum)plane, + (GLfloat *)equation + ); + +exit: + if (_array) { + releasePointer(_env, _array, equation, JNI_FALSE); + } +} + +/* void glClipPlanex ( GLenum plane, const GLfixed *equation ) */ +static void +android_glClipPlanex__I_3II + (JNIEnv *_env, jobject _this, jint plane, jintArray equation_ref, jint offset) { + GLfixed *equation_base = (GLfixed *) 0; + jint _remaining; + GLfixed *equation = (GLfixed *) 0; + + if (!equation_ref) { + _env->ThrowNew(IAEClass, "equation == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(equation_ref) - offset; + if (_remaining < 4) { + _env->ThrowNew(IAEClass, "length - offset < 4"); + goto exit; + } + equation_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(equation_ref, (jboolean *)0); + equation = equation_base + offset; + + glClipPlanex( + (GLenum)plane, + (GLfixed *)equation + ); + +exit: + if (equation_base) { + _env->ReleasePrimitiveArrayCritical(equation_ref, equation_base, + JNI_ABORT); + } +} + +/* void glClipPlanex ( GLenum plane, const GLfixed *equation ) */ +static void +android_glClipPlanex__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint plane, jobject equation_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *equation = (GLfixed *) 0; + + equation = (GLfixed *)getPointer(_env, equation_buf, &_array, &_remaining); + if (_remaining < 4) { + _env->ThrowNew(IAEClass, "remaining() < 4"); + goto exit; + } + glClipPlanex( + (GLenum)plane, + (GLfixed *)equation + ); + +exit: + if (_array) { + releasePointer(_env, _array, equation, JNI_FALSE); + } +} + +/* void glColor4ub ( GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha ) */ +static void +android_glColor4ub__BBBB + (JNIEnv *_env, jobject _this, jbyte red, jbyte green, jbyte blue, jbyte alpha) { + glColor4ub( + (GLubyte)red, + (GLubyte)green, + (GLubyte)blue, + (GLubyte)alpha + ); +} + +/* void glColorPointer ( GLint size, GLenum type, GLsizei stride, GLint offset ) */ +static void +android_glColorPointer__IIII + (JNIEnv *_env, jobject _this, jint size, jint type, jint stride, jint offset) { + glColorPointer( + (GLint)size, + (GLenum)type, + (GLsizei)stride, + (const GLvoid *)offset + ); +} + +/* void glDeleteBuffers ( GLsizei n, const GLuint *buffers ) */ +static void +android_glDeleteBuffers__I_3II + (JNIEnv *_env, jobject _this, jint n, jintArray buffers_ref, jint offset) { + GLuint *buffers_base = (GLuint *) 0; + jint _remaining; + GLuint *buffers = (GLuint *) 0; + + if (!buffers_ref) { + _env->ThrowNew(IAEClass, "buffers == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(buffers_ref) - offset; + if (_remaining < n) { + _env->ThrowNew(IAEClass, "length - offset < n"); + goto exit; + } + buffers_base = (GLuint *) + _env->GetPrimitiveArrayCritical(buffers_ref, (jboolean *)0); + buffers = buffers_base + offset; + + glDeleteBuffers( + (GLsizei)n, + (GLuint *)buffers + ); + +exit: + if (buffers_base) { + _env->ReleasePrimitiveArrayCritical(buffers_ref, buffers_base, + JNI_ABORT); + } +} + +/* void glDeleteBuffers ( GLsizei n, const GLuint *buffers ) */ +static void +android_glDeleteBuffers__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint n, jobject buffers_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLuint *buffers = (GLuint *) 0; + + buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining); + if (_remaining < n) { + _env->ThrowNew(IAEClass, "remaining() < n"); + goto exit; + } + glDeleteBuffers( + (GLsizei)n, + (GLuint *)buffers + ); + +exit: + if (_array) { + releasePointer(_env, _array, buffers, JNI_FALSE); + } +} + +/* void glDrawElements ( GLenum mode, GLsizei count, GLenum type, GLint offset ) */ +static void +android_glDrawElements__IIII + (JNIEnv *_env, jobject _this, jint mode, jint count, jint type, jint offset) { + glDrawElements( + (GLenum)mode, + (GLsizei)count, + (GLenum)type, + (const GLvoid *)offset + ); +} + +/* void glGenBuffers ( GLsizei n, GLuint *buffers ) */ +static void +android_glGenBuffers__I_3II + (JNIEnv *_env, jobject _this, jint n, jintArray buffers_ref, jint offset) { + jint _exception = 0; + GLuint *buffers_base = (GLuint *) 0; + jint _remaining; + GLuint *buffers = (GLuint *) 0; + + if (!buffers_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "buffers == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(buffers_ref) - offset; + if (_remaining < n) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < n"); + goto exit; + } + buffers_base = (GLuint *) + _env->GetPrimitiveArrayCritical(buffers_ref, (jboolean *)0); + buffers = buffers_base + offset; + + glGenBuffers( + (GLsizei)n, + (GLuint *)buffers + ); + +exit: + if (buffers_base) { + _env->ReleasePrimitiveArrayCritical(buffers_ref, buffers_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGenBuffers ( GLsizei n, GLuint *buffers ) */ +static void +android_glGenBuffers__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint n, jobject buffers_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLuint *buffers = (GLuint *) 0; + + buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining); + if (_remaining < n) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < n"); + goto exit; + } + glGenBuffers( + (GLsizei)n, + (GLuint *)buffers + ); + +exit: + if (_array) { + releasePointer(_env, _array, buffers, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetBooleanv ( GLenum pname, GLboolean *params ) */ +static void +android_glGetBooleanv__I_3ZI + (JNIEnv *_env, jobject _this, jint pname, jbooleanArray params_ref, jint offset) { + jint _exception = 0; + GLboolean *params_base = (GLboolean *) 0; + jint _remaining; + GLboolean *params = (GLboolean *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + params_base = (GLboolean *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetBooleanv( + (GLenum)pname, + (GLboolean *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetBooleanv ( GLenum pname, GLboolean *params ) */ +static void +android_glGetBooleanv__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLboolean *params = (GLboolean *) 0; + + params = (GLboolean *)getPointer(_env, params_buf, &_array, &_remaining); + glGetBooleanv( + (GLenum)pname, + (GLboolean *)params + ); + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetBufferParameteriv ( GLenum target, GLenum pname, GLint *params ) */ +static void +android_glGetBufferParameteriv__II_3II + (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glGetBufferParameteriv"); +} + +/* void glGetBufferParameteriv ( GLenum target, GLenum pname, GLint *params ) */ +static void +android_glGetBufferParameteriv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { + _env->ThrowNew(UOEClass, + "glGetBufferParameteriv"); +} + +/* void glGetClipPlanef ( GLenum pname, GLfloat *eqn ) */ +static void +android_glGetClipPlanef__I_3FI + (JNIEnv *_env, jobject _this, jint pname, jfloatArray eqn_ref, jint offset) { + jint _exception = 0; + GLfloat *eqn_base = (GLfloat *) 0; + jint _remaining; + GLfloat *eqn = (GLfloat *) 0; + + if (!eqn_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "eqn == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(eqn_ref) - offset; + eqn_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(eqn_ref, (jboolean *)0); + eqn = eqn_base + offset; + + glGetClipPlanef( + (GLenum)pname, + (GLfloat *)eqn + ); + +exit: + if (eqn_base) { + _env->ReleasePrimitiveArrayCritical(eqn_ref, eqn_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetClipPlanef ( GLenum pname, GLfloat *eqn ) */ +static void +android_glGetClipPlanef__ILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject eqn_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *eqn = (GLfloat *) 0; + + eqn = (GLfloat *)getPointer(_env, eqn_buf, &_array, &_remaining); + glGetClipPlanef( + (GLenum)pname, + (GLfloat *)eqn + ); + if (_array) { + releasePointer(_env, _array, eqn, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetClipPlanex ( GLenum pname, GLfixed *eqn ) */ +static void +android_glGetClipPlanex__I_3II + (JNIEnv *_env, jobject _this, jint pname, jintArray eqn_ref, jint offset) { + jint _exception = 0; + GLfixed *eqn_base = (GLfixed *) 0; + jint _remaining; + GLfixed *eqn = (GLfixed *) 0; + + if (!eqn_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "eqn == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(eqn_ref) - offset; + eqn_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(eqn_ref, (jboolean *)0); + eqn = eqn_base + offset; + + glGetClipPlanex( + (GLenum)pname, + (GLfixed *)eqn + ); + +exit: + if (eqn_base) { + _env->ReleasePrimitiveArrayCritical(eqn_ref, eqn_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetClipPlanex ( GLenum pname, GLfixed *eqn ) */ +static void +android_glGetClipPlanex__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject eqn_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *eqn = (GLfixed *) 0; + + eqn = (GLfixed *)getPointer(_env, eqn_buf, &_array, &_remaining); + glGetClipPlanex( + (GLenum)pname, + (GLfixed *)eqn + ); + if (_array) { + releasePointer(_env, _array, eqn, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetFixedv ( GLenum pname, GLfixed *params ) */ +static void +android_glGetFixedv__I_3II + (JNIEnv *_env, jobject _this, jint pname, jintArray params_ref, jint offset) { + jint _exception = 0; + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetFixedv( + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetFixedv ( GLenum pname, GLfixed *params ) */ +static void +android_glGetFixedv__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + glGetFixedv( + (GLenum)pname, + (GLfixed *)params + ); + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetFloatv ( GLenum pname, GLfloat *params ) */ +static void +android_glGetFloatv__I_3FI + (JNIEnv *_env, jobject _this, jint pname, jfloatArray params_ref, jint offset) { + jint _exception = 0; + GLfloat *params_base = (GLfloat *) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + params_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetFloatv( + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetFloatv ( GLenum pname, GLfloat *params ) */ +static void +android_glGetFloatv__ILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining); + glGetFloatv( + (GLenum)pname, + (GLfloat *)params + ); + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetLightfv ( GLenum light, GLenum pname, GLfloat *params ) */ +static void +android_glGetLightfv__II_3FI + (JNIEnv *_env, jobject _this, jint light, jint pname, jfloatArray params_ref, jint offset) { + jint _exception = 0; + GLfloat *params_base = (GLfloat *) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_SPOT_EXPONENT) + case GL_SPOT_EXPONENT: +#endif // defined(GL_SPOT_EXPONENT) +#if defined(GL_SPOT_CUTOFF) + case GL_SPOT_CUTOFF: +#endif // defined(GL_SPOT_CUTOFF) +#if defined(GL_CONSTANT_ATTENUATION) + case GL_CONSTANT_ATTENUATION: +#endif // defined(GL_CONSTANT_ATTENUATION) +#if defined(GL_LINEAR_ATTENUATION) + case GL_LINEAR_ATTENUATION: +#endif // defined(GL_LINEAR_ATTENUATION) +#if defined(GL_QUADRATIC_ATTENUATION) + case GL_QUADRATIC_ATTENUATION: +#endif // defined(GL_QUADRATIC_ATTENUATION) + _needed = 1; + break; +#if defined(GL_SPOT_DIRECTION) + case GL_SPOT_DIRECTION: +#endif // defined(GL_SPOT_DIRECTION) + _needed = 3; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetLightfv( + (GLenum)light, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetLightfv ( GLenum light, GLenum pname, GLfloat *params ) */ +static void +android_glGetLightfv__IILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint light, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_SPOT_EXPONENT) + case GL_SPOT_EXPONENT: +#endif // defined(GL_SPOT_EXPONENT) +#if defined(GL_SPOT_CUTOFF) + case GL_SPOT_CUTOFF: +#endif // defined(GL_SPOT_CUTOFF) +#if defined(GL_CONSTANT_ATTENUATION) + case GL_CONSTANT_ATTENUATION: +#endif // defined(GL_CONSTANT_ATTENUATION) +#if defined(GL_LINEAR_ATTENUATION) + case GL_LINEAR_ATTENUATION: +#endif // defined(GL_LINEAR_ATTENUATION) +#if defined(GL_QUADRATIC_ATTENUATION) + case GL_QUADRATIC_ATTENUATION: +#endif // defined(GL_QUADRATIC_ATTENUATION) + _needed = 1; + break; +#if defined(GL_SPOT_DIRECTION) + case GL_SPOT_DIRECTION: +#endif // defined(GL_SPOT_DIRECTION) + _needed = 3; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glGetLightfv( + (GLenum)light, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetLightxv ( GLenum light, GLenum pname, GLfixed *params ) */ +static void +android_glGetLightxv__II_3II + (JNIEnv *_env, jobject _this, jint light, jint pname, jintArray params_ref, jint offset) { + jint _exception = 0; + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_SPOT_EXPONENT) + case GL_SPOT_EXPONENT: +#endif // defined(GL_SPOT_EXPONENT) +#if defined(GL_SPOT_CUTOFF) + case GL_SPOT_CUTOFF: +#endif // defined(GL_SPOT_CUTOFF) +#if defined(GL_CONSTANT_ATTENUATION) + case GL_CONSTANT_ATTENUATION: +#endif // defined(GL_CONSTANT_ATTENUATION) +#if defined(GL_LINEAR_ATTENUATION) + case GL_LINEAR_ATTENUATION: +#endif // defined(GL_LINEAR_ATTENUATION) +#if defined(GL_QUADRATIC_ATTENUATION) + case GL_QUADRATIC_ATTENUATION: +#endif // defined(GL_QUADRATIC_ATTENUATION) + _needed = 1; + break; +#if defined(GL_SPOT_DIRECTION) + case GL_SPOT_DIRECTION: +#endif // defined(GL_SPOT_DIRECTION) + _needed = 3; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetLightxv( + (GLenum)light, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetLightxv ( GLenum light, GLenum pname, GLfixed *params ) */ +static void +android_glGetLightxv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint light, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_SPOT_EXPONENT) + case GL_SPOT_EXPONENT: +#endif // defined(GL_SPOT_EXPONENT) +#if defined(GL_SPOT_CUTOFF) + case GL_SPOT_CUTOFF: +#endif // defined(GL_SPOT_CUTOFF) +#if defined(GL_CONSTANT_ATTENUATION) + case GL_CONSTANT_ATTENUATION: +#endif // defined(GL_CONSTANT_ATTENUATION) +#if defined(GL_LINEAR_ATTENUATION) + case GL_LINEAR_ATTENUATION: +#endif // defined(GL_LINEAR_ATTENUATION) +#if defined(GL_QUADRATIC_ATTENUATION) + case GL_QUADRATIC_ATTENUATION: +#endif // defined(GL_QUADRATIC_ATTENUATION) + _needed = 1; + break; +#if defined(GL_SPOT_DIRECTION) + case GL_SPOT_DIRECTION: +#endif // defined(GL_SPOT_DIRECTION) + _needed = 3; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glGetLightxv( + (GLenum)light, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetMaterialfv ( GLenum face, GLenum pname, GLfloat *params ) */ +static void +android_glGetMaterialfv__II_3FI + (JNIEnv *_env, jobject _this, jint face, jint pname, jfloatArray params_ref, jint offset) { + jint _exception = 0; + GLfloat *params_base = (GLfloat *) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_SHININESS) + case GL_SHININESS: +#endif // defined(GL_SHININESS) + _needed = 1; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) +#if defined(GL_AMBIENT_AND_DIFFUSE) + case GL_AMBIENT_AND_DIFFUSE: +#endif // defined(GL_AMBIENT_AND_DIFFUSE) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetMaterialfv( + (GLenum)face, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetMaterialfv ( GLenum face, GLenum pname, GLfloat *params ) */ +static void +android_glGetMaterialfv__IILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint face, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_SHININESS) + case GL_SHININESS: +#endif // defined(GL_SHININESS) + _needed = 1; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) +#if defined(GL_AMBIENT_AND_DIFFUSE) + case GL_AMBIENT_AND_DIFFUSE: +#endif // defined(GL_AMBIENT_AND_DIFFUSE) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glGetMaterialfv( + (GLenum)face, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetMaterialxv ( GLenum face, GLenum pname, GLfixed *params ) */ +static void +android_glGetMaterialxv__II_3II + (JNIEnv *_env, jobject _this, jint face, jint pname, jintArray params_ref, jint offset) { + jint _exception = 0; + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_SHININESS) + case GL_SHININESS: +#endif // defined(GL_SHININESS) + _needed = 1; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) +#if defined(GL_AMBIENT_AND_DIFFUSE) + case GL_AMBIENT_AND_DIFFUSE: +#endif // defined(GL_AMBIENT_AND_DIFFUSE) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetMaterialxv( + (GLenum)face, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetMaterialxv ( GLenum face, GLenum pname, GLfixed *params ) */ +static void +android_glGetMaterialxv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint face, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_SHININESS) + case GL_SHININESS: +#endif // defined(GL_SHININESS) + _needed = 1; + break; +#if defined(GL_AMBIENT) + case GL_AMBIENT: +#endif // defined(GL_AMBIENT) +#if defined(GL_DIFFUSE) + case GL_DIFFUSE: +#endif // defined(GL_DIFFUSE) +#if defined(GL_SPECULAR) + case GL_SPECULAR: +#endif // defined(GL_SPECULAR) +#if defined(GL_EMISSION) + case GL_EMISSION: +#endif // defined(GL_EMISSION) +#if defined(GL_AMBIENT_AND_DIFFUSE) + case GL_AMBIENT_AND_DIFFUSE: +#endif // defined(GL_AMBIENT_AND_DIFFUSE) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glGetMaterialxv( + (GLenum)face, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetTexEnviv ( GLenum env, GLenum pname, GLint *params ) */ +static void +android_glGetTexEnviv__II_3II + (JNIEnv *_env, jobject _this, jint env, jint pname, jintArray params_ref, jint offset) { + jint _exception = 0; + GLint *params_base = (GLint *) 0; + jint _remaining; + GLint *params = (GLint *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_TEXTURE_ENV_MODE) + case GL_TEXTURE_ENV_MODE: +#endif // defined(GL_TEXTURE_ENV_MODE) +#if defined(GL_COMBINE_RGB) + case GL_COMBINE_RGB: +#endif // defined(GL_COMBINE_RGB) +#if defined(GL_COMBINE_ALPHA) + case GL_COMBINE_ALPHA: +#endif // defined(GL_COMBINE_ALPHA) + _needed = 1; + break; +#if defined(GL_TEXTURE_ENV_COLOR) + case GL_TEXTURE_ENV_COLOR: +#endif // defined(GL_TEXTURE_ENV_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLint *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetTexEnviv( + (GLenum)env, + (GLenum)pname, + (GLint *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetTexEnviv ( GLenum env, GLenum pname, GLint *params ) */ +static void +android_glGetTexEnviv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint env, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLint *params = (GLint *) 0; + + params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_TEXTURE_ENV_MODE) + case GL_TEXTURE_ENV_MODE: +#endif // defined(GL_TEXTURE_ENV_MODE) +#if defined(GL_COMBINE_RGB) + case GL_COMBINE_RGB: +#endif // defined(GL_COMBINE_RGB) +#if defined(GL_COMBINE_ALPHA) + case GL_COMBINE_ALPHA: +#endif // defined(GL_COMBINE_ALPHA) + _needed = 1; + break; +#if defined(GL_TEXTURE_ENV_COLOR) + case GL_TEXTURE_ENV_COLOR: +#endif // defined(GL_TEXTURE_ENV_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glGetTexEnviv( + (GLenum)env, + (GLenum)pname, + (GLint *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetTexEnvxv ( GLenum env, GLenum pname, GLfixed *params ) */ +static void +android_glGetTexEnvxv__II_3II + (JNIEnv *_env, jobject _this, jint env, jint pname, jintArray params_ref, jint offset) { + jint _exception = 0; + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_TEXTURE_ENV_MODE) + case GL_TEXTURE_ENV_MODE: +#endif // defined(GL_TEXTURE_ENV_MODE) +#if defined(GL_COMBINE_RGB) + case GL_COMBINE_RGB: +#endif // defined(GL_COMBINE_RGB) +#if defined(GL_COMBINE_ALPHA) + case GL_COMBINE_ALPHA: +#endif // defined(GL_COMBINE_ALPHA) + _needed = 1; + break; +#if defined(GL_TEXTURE_ENV_COLOR) + case GL_TEXTURE_ENV_COLOR: +#endif // defined(GL_TEXTURE_ENV_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetTexEnvxv( + (GLenum)env, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetTexEnvxv ( GLenum env, GLenum pname, GLfixed *params ) */ +static void +android_glGetTexEnvxv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint env, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_TEXTURE_ENV_MODE) + case GL_TEXTURE_ENV_MODE: +#endif // defined(GL_TEXTURE_ENV_MODE) +#if defined(GL_COMBINE_RGB) + case GL_COMBINE_RGB: +#endif // defined(GL_COMBINE_RGB) +#if defined(GL_COMBINE_ALPHA) + case GL_COMBINE_ALPHA: +#endif // defined(GL_COMBINE_ALPHA) + _needed = 1; + break; +#if defined(GL_TEXTURE_ENV_COLOR) + case GL_TEXTURE_ENV_COLOR: +#endif // defined(GL_TEXTURE_ENV_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glGetTexEnvxv( + (GLenum)env, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetTexParameterfv ( GLenum target, GLenum pname, GLfloat *params ) */ +static void +android_glGetTexParameterfv__II_3FI + (JNIEnv *_env, jobject _this, jint target, jint pname, jfloatArray params_ref, jint offset) { + jint _exception = 0; + GLfloat *params_base = (GLfloat *) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + if (_remaining < 1) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < 1"); + goto exit; + } + params_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetTexParameterfv( + (GLenum)target, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetTexParameterfv ( GLenum target, GLenum pname, GLfloat *params ) */ +static void +android_glGetTexParameterfv__IILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining); + if (_remaining < 1) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < 1"); + goto exit; + } + glGetTexParameterfv( + (GLenum)target, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetTexParameteriv ( GLenum target, GLenum pname, GLint *params ) */ +static void +android_glGetTexParameteriv__II_3II + (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) { + jint _exception = 0; + GLint *params_base = (GLint *) 0; + jint _remaining; + GLint *params = (GLint *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + if (_remaining < 1) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < 1"); + goto exit; + } + params_base = (GLint *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetTexParameteriv( + (GLenum)target, + (GLenum)pname, + (GLint *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetTexParameteriv ( GLenum target, GLenum pname, GLint *params ) */ +static void +android_glGetTexParameteriv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLint *params = (GLint *) 0; + + params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining); + if (_remaining < 1) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < 1"); + goto exit; + } + glGetTexParameteriv( + (GLenum)target, + (GLenum)pname, + (GLint *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* void glGetTexParameterxv ( GLenum target, GLenum pname, GLfixed *params ) */ +static void +android_glGetTexParameterxv__II_3II + (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) { + jint _exception = 0; + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _exception = 1; + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _exception = 1; + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + if (_remaining < 1) { + _exception = 1; + _env->ThrowNew(IAEClass, "length - offset < 1"); + goto exit; + } + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glGetTexParameterxv( + (GLenum)target, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + _exception ? JNI_ABORT: 0); + } +} + +/* void glGetTexParameterxv ( GLenum target, GLenum pname, GLfixed *params ) */ +static void +android_glGetTexParameterxv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { + jint _exception = 0; + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + if (_remaining < 1) { + _exception = 1; + _env->ThrowNew(IAEClass, "remaining() < 1"); + goto exit; + } + glGetTexParameterxv( + (GLenum)target, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE); + } +} + +/* GLboolean glIsBuffer ( GLuint buffer ) */ +static jboolean +android_glIsBuffer__I + (JNIEnv *_env, jobject _this, jint buffer) { + GLboolean _returnValue; + _returnValue = glIsBuffer( + (GLuint)buffer + ); + return _returnValue; +} + +/* GLboolean glIsEnabled ( GLenum cap ) */ +static jboolean +android_glIsEnabled__I + (JNIEnv *_env, jobject _this, jint cap) { + GLboolean _returnValue; + _returnValue = glIsEnabled( + (GLenum)cap + ); + return _returnValue; +} + +/* GLboolean glIsTexture ( GLuint texture ) */ +static jboolean +android_glIsTexture__I + (JNIEnv *_env, jobject _this, jint texture) { + GLboolean _returnValue; + _returnValue = glIsTexture( + (GLuint)texture + ); + return _returnValue; +} + +/* void glNormalPointer ( GLenum type, GLsizei stride, GLint offset ) */ +static void +android_glNormalPointer__III + (JNIEnv *_env, jobject _this, jint type, jint stride, jint offset) { + glNormalPointer( + (GLenum)type, + (GLsizei)stride, + (const GLvoid *)offset + ); +} + +/* void glPointParameterf ( GLenum pname, GLfloat param ) */ +static void +android_glPointParameterf__IF + (JNIEnv *_env, jobject _this, jint pname, jfloat param) { + glPointParameterf( + (GLenum)pname, + (GLfloat)param + ); +} + +/* void glPointParameterfv ( GLenum pname, const GLfloat *params ) */ +static void +android_glPointParameterfv__I_3FI + (JNIEnv *_env, jobject _this, jint pname, jfloatArray params_ref, jint offset) { + GLfloat *params_base = (GLfloat *) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + if (_remaining < 1) { + _env->ThrowNew(IAEClass, "length - offset < 1"); + goto exit; + } + params_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glPointParameterfv( + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glPointParameterfv ( GLenum pname, const GLfloat *params ) */ +static void +android_glPointParameterfv__ILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining); + if (_remaining < 1) { + _env->ThrowNew(IAEClass, "remaining() < 1"); + goto exit; + } + glPointParameterfv( + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glPointParameterx ( GLenum pname, GLfixed param ) */ +static void +android_glPointParameterx__II + (JNIEnv *_env, jobject _this, jint pname, jint param) { + glPointParameterx( + (GLenum)pname, + (GLfixed)param + ); +} + +/* void glPointParameterxv ( GLenum pname, const GLfixed *params ) */ +static void +android_glPointParameterxv__I_3II + (JNIEnv *_env, jobject _this, jint pname, jintArray params_ref, jint offset) { + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + if (_remaining < 1) { + _env->ThrowNew(IAEClass, "length - offset < 1"); + goto exit; + } + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glPointParameterxv( + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glPointParameterxv ( GLenum pname, const GLfixed *params ) */ +static void +android_glPointParameterxv__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + if (_remaining < 1) { + _env->ThrowNew(IAEClass, "remaining() < 1"); + goto exit; + } + glPointParameterxv( + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glPointSizePointerOES ( GLenum type, GLsizei stride, const GLvoid *pointer ) */ +static void +android_glPointSizePointerOES__IILjava_nio_Buffer_2 + (JNIEnv *_env, jobject _this, jint type, jint stride, jobject pointer_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLvoid *pointer = (GLvoid *) 0; + + pointer = (GLvoid *)getPointer(_env, pointer_buf, &_array, &_remaining); + glPointSizePointerOES( + (GLenum)type, + (GLsizei)stride, + (GLvoid *)pointer + ); + if (_array) { + releasePointer(_env, _array, pointer, JNI_FALSE); + } +} + +/* void glTexCoordPointer ( GLint size, GLenum type, GLsizei stride, GLint offset ) */ +static void +android_glTexCoordPointer__IIII + (JNIEnv *_env, jobject _this, jint size, jint type, jint stride, jint offset) { + glTexCoordPointer( + (GLint)size, + (GLenum)type, + (GLsizei)stride, + (const GLvoid *)offset + ); +} + +/* void glTexEnvi ( GLenum target, GLenum pname, GLint param ) */ +static void +android_glTexEnvi__III + (JNIEnv *_env, jobject _this, jint target, jint pname, jint param) { + glTexEnvi( + (GLenum)target, + (GLenum)pname, + (GLint)param + ); +} + +/* void glTexEnviv ( GLenum target, GLenum pname, const GLint *params ) */ +static void +android_glTexEnviv__II_3II + (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) { + GLint *params_base = (GLint *) 0; + jint _remaining; + GLint *params = (GLint *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + int _needed; + switch (pname) { +#if defined(GL_TEXTURE_ENV_MODE) + case GL_TEXTURE_ENV_MODE: +#endif // defined(GL_TEXTURE_ENV_MODE) +#if defined(GL_COMBINE_RGB) + case GL_COMBINE_RGB: +#endif // defined(GL_COMBINE_RGB) +#if defined(GL_COMBINE_ALPHA) + case GL_COMBINE_ALPHA: +#endif // defined(GL_COMBINE_ALPHA) + _needed = 1; + break; +#if defined(GL_TEXTURE_ENV_COLOR) + case GL_TEXTURE_ENV_COLOR: +#endif // defined(GL_TEXTURE_ENV_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "length - offset < needed"); + goto exit; + } + params_base = (GLint *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glTexEnviv( + (GLenum)target, + (GLenum)pname, + (GLint *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glTexEnviv ( GLenum target, GLenum pname, const GLint *params ) */ +static void +android_glTexEnviv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLint *params = (GLint *) 0; + + params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining); + int _needed; + switch (pname) { +#if defined(GL_TEXTURE_ENV_MODE) + case GL_TEXTURE_ENV_MODE: +#endif // defined(GL_TEXTURE_ENV_MODE) +#if defined(GL_COMBINE_RGB) + case GL_COMBINE_RGB: +#endif // defined(GL_COMBINE_RGB) +#if defined(GL_COMBINE_ALPHA) + case GL_COMBINE_ALPHA: +#endif // defined(GL_COMBINE_ALPHA) + _needed = 1; + break; +#if defined(GL_TEXTURE_ENV_COLOR) + case GL_TEXTURE_ENV_COLOR: +#endif // defined(GL_TEXTURE_ENV_COLOR) + _needed = 4; + break; + default: + _needed = 0; + break; + } + if (_remaining < _needed) { + _env->ThrowNew(IAEClass, "remaining() < needed"); + goto exit; + } + glTexEnviv( + (GLenum)target, + (GLenum)pname, + (GLint *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glTexParameterfv ( GLenum target, GLenum pname, const GLfloat *params ) */ +static void +android_glTexParameterfv__II_3FI + (JNIEnv *_env, jobject _this, jint target, jint pname, jfloatArray params_ref, jint offset) { + GLfloat *params_base = (GLfloat *) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + if (_remaining < 1) { + _env->ThrowNew(IAEClass, "length - offset < 1"); + goto exit; + } + params_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glTexParameterfv( + (GLenum)target, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glTexParameterfv ( GLenum target, GLenum pname, const GLfloat *params ) */ +static void +android_glTexParameterfv__IILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *params = (GLfloat *) 0; + + params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining); + if (_remaining < 1) { + _env->ThrowNew(IAEClass, "remaining() < 1"); + goto exit; + } + glTexParameterfv( + (GLenum)target, + (GLenum)pname, + (GLfloat *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glTexParameteri ( GLenum target, GLenum pname, GLint param ) */ +static void +android_glTexParameteri__III + (JNIEnv *_env, jobject _this, jint target, jint pname, jint param) { + glTexParameteri( + (GLenum)target, + (GLenum)pname, + (GLint)param + ); +} + +/* void glTexParameteriv ( GLenum target, GLenum pname, const GLint *params ) */ +static void +android_glTexParameteriv__II_3II + (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) { + GLint *params_base = (GLint *) 0; + jint _remaining; + GLint *params = (GLint *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + if (_remaining < 1) { + _env->ThrowNew(IAEClass, "length - offset < 1"); + goto exit; + } + params_base = (GLint *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glTexParameteriv( + (GLenum)target, + (GLenum)pname, + (GLint *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glTexParameteriv ( GLenum target, GLenum pname, const GLint *params ) */ +static void +android_glTexParameteriv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLint *params = (GLint *) 0; + + params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining); + if (_remaining < 1) { + _env->ThrowNew(IAEClass, "remaining() < 1"); + goto exit; + } + glTexParameteriv( + (GLenum)target, + (GLenum)pname, + (GLint *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glTexParameterxv ( GLenum target, GLenum pname, const GLfixed *params ) */ +static void +android_glTexParameterxv__II_3II + (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) { + GLfixed *params_base = (GLfixed *) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + if (!params_ref) { + _env->ThrowNew(IAEClass, "params == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(params_ref) - offset; + if (_remaining < 1) { + _env->ThrowNew(IAEClass, "length - offset < 1"); + goto exit; + } + params_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0); + params = params_base + offset; + + glTexParameterxv( + (GLenum)target, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (params_base) { + _env->ReleasePrimitiveArrayCritical(params_ref, params_base, + JNI_ABORT); + } +} + +/* void glTexParameterxv ( GLenum target, GLenum pname, const GLfixed *params ) */ +static void +android_glTexParameterxv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *params = (GLfixed *) 0; + + params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining); + if (_remaining < 1) { + _env->ThrowNew(IAEClass, "remaining() < 1"); + goto exit; + } + glTexParameterxv( + (GLenum)target, + (GLenum)pname, + (GLfixed *)params + ); + +exit: + if (_array) { + releasePointer(_env, _array, params, JNI_FALSE); + } +} + +/* void glVertexPointer ( GLint size, GLenum type, GLsizei stride, GLint offset ) */ +static void +android_glVertexPointer__IIII + (JNIEnv *_env, jobject _this, jint size, jint type, jint stride, jint offset) { + glVertexPointer( + (GLint)size, + (GLenum)type, + (GLsizei)stride, + (const GLvoid *)offset + ); +} + +/* void glCurrentPaletteMatrixOES ( GLuint matrixpaletteindex ) */ +static void +android_glCurrentPaletteMatrixOES__I + (JNIEnv *_env, jobject _this, jint matrixpaletteindex) { + _env->ThrowNew(UOEClass, + "glCurrentPaletteMatrixOES"); +} + +/* void glDrawTexfOES ( GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height ) */ +static void +android_glDrawTexfOES__FFFFF + (JNIEnv *_env, jobject _this, jfloat x, jfloat y, jfloat z, jfloat width, jfloat height) { + glDrawTexfOES( + (GLfloat)x, + (GLfloat)y, + (GLfloat)z, + (GLfloat)width, + (GLfloat)height + ); +} + +/* void glDrawTexfvOES ( const GLfloat *coords ) */ +static void +android_glDrawTexfvOES___3FI + (JNIEnv *_env, jobject _this, jfloatArray coords_ref, jint offset) { + GLfloat *coords_base = (GLfloat *) 0; + jint _remaining; + GLfloat *coords = (GLfloat *) 0; + + if (!coords_ref) { + _env->ThrowNew(IAEClass, "coords == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(coords_ref) - offset; + if (_remaining < 5) { + _env->ThrowNew(IAEClass, "length - offset < 5"); + goto exit; + } + coords_base = (GLfloat *) + _env->GetPrimitiveArrayCritical(coords_ref, (jboolean *)0); + coords = coords_base + offset; + + glDrawTexfvOES( + (GLfloat *)coords + ); + +exit: + if (coords_base) { + _env->ReleasePrimitiveArrayCritical(coords_ref, coords_base, + JNI_ABORT); + } +} + +/* void glDrawTexfvOES ( const GLfloat *coords ) */ +static void +android_glDrawTexfvOES__Ljava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jobject coords_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfloat *coords = (GLfloat *) 0; + + coords = (GLfloat *)getPointer(_env, coords_buf, &_array, &_remaining); + if (_remaining < 5) { + _env->ThrowNew(IAEClass, "remaining() < 5"); + goto exit; + } + glDrawTexfvOES( + (GLfloat *)coords + ); + +exit: + if (_array) { + releasePointer(_env, _array, coords, JNI_FALSE); + } +} + +/* void glDrawTexiOES ( GLint x, GLint y, GLint z, GLint width, GLint height ) */ +static void +android_glDrawTexiOES__IIIII + (JNIEnv *_env, jobject _this, jint x, jint y, jint z, jint width, jint height) { + glDrawTexiOES( + (GLint)x, + (GLint)y, + (GLint)z, + (GLint)width, + (GLint)height + ); +} + +/* void glDrawTexivOES ( const GLint *coords ) */ +static void +android_glDrawTexivOES___3II + (JNIEnv *_env, jobject _this, jintArray coords_ref, jint offset) { + GLint *coords_base = (GLint *) 0; + jint _remaining; + GLint *coords = (GLint *) 0; + + if (!coords_ref) { + _env->ThrowNew(IAEClass, "coords == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(coords_ref) - offset; + if (_remaining < 5) { + _env->ThrowNew(IAEClass, "length - offset < 5"); + goto exit; + } + coords_base = (GLint *) + _env->GetPrimitiveArrayCritical(coords_ref, (jboolean *)0); + coords = coords_base + offset; + + glDrawTexivOES( + (GLint *)coords + ); + +exit: + if (coords_base) { + _env->ReleasePrimitiveArrayCritical(coords_ref, coords_base, + JNI_ABORT); + } +} + +/* void glDrawTexivOES ( const GLint *coords ) */ +static void +android_glDrawTexivOES__Ljava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jobject coords_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLint *coords = (GLint *) 0; + + coords = (GLint *)getPointer(_env, coords_buf, &_array, &_remaining); + if (_remaining < 5) { + _env->ThrowNew(IAEClass, "remaining() < 5"); + goto exit; + } + glDrawTexivOES( + (GLint *)coords + ); + +exit: + if (_array) { + releasePointer(_env, _array, coords, JNI_FALSE); + } +} + +/* void glDrawTexsOES ( GLshort x, GLshort y, GLshort z, GLshort width, GLshort height ) */ +static void +android_glDrawTexsOES__SSSSS + (JNIEnv *_env, jobject _this, jshort x, jshort y, jshort z, jshort width, jshort height) { + glDrawTexsOES( + (GLshort)x, + (GLshort)y, + (GLshort)z, + (GLshort)width, + (GLshort)height + ); +} + +/* void glDrawTexsvOES ( const GLshort *coords ) */ +static void +android_glDrawTexsvOES___3SI + (JNIEnv *_env, jobject _this, jshortArray coords_ref, jint offset) { + GLshort *coords_base = (GLshort *) 0; + jint _remaining; + GLshort *coords = (GLshort *) 0; + + if (!coords_ref) { + _env->ThrowNew(IAEClass, "coords == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(coords_ref) - offset; + if (_remaining < 5) { + _env->ThrowNew(IAEClass, "length - offset < 5"); + goto exit; + } + coords_base = (GLshort *) + _env->GetPrimitiveArrayCritical(coords_ref, (jboolean *)0); + coords = coords_base + offset; + + glDrawTexsvOES( + (GLshort *)coords + ); + +exit: + if (coords_base) { + _env->ReleasePrimitiveArrayCritical(coords_ref, coords_base, + JNI_ABORT); + } +} + +/* void glDrawTexsvOES ( const GLshort *coords ) */ +static void +android_glDrawTexsvOES__Ljava_nio_ShortBuffer_2 + (JNIEnv *_env, jobject _this, jobject coords_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLshort *coords = (GLshort *) 0; + + coords = (GLshort *)getPointer(_env, coords_buf, &_array, &_remaining); + if (_remaining < 5) { + _env->ThrowNew(IAEClass, "remaining() < 5"); + goto exit; + } + glDrawTexsvOES( + (GLshort *)coords + ); + +exit: + if (_array) { + releasePointer(_env, _array, coords, JNI_FALSE); + } +} + +/* void glDrawTexxOES ( GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height ) */ +static void +android_glDrawTexxOES__IIIII + (JNIEnv *_env, jobject _this, jint x, jint y, jint z, jint width, jint height) { + glDrawTexxOES( + (GLfixed)x, + (GLfixed)y, + (GLfixed)z, + (GLfixed)width, + (GLfixed)height + ); +} + +/* void glDrawTexxvOES ( const GLfixed *coords ) */ +static void +android_glDrawTexxvOES___3II + (JNIEnv *_env, jobject _this, jintArray coords_ref, jint offset) { + GLfixed *coords_base = (GLfixed *) 0; + jint _remaining; + GLfixed *coords = (GLfixed *) 0; + + if (!coords_ref) { + _env->ThrowNew(IAEClass, "coords == null"); + goto exit; + } + if (offset < 0) { + _env->ThrowNew(IAEClass, "offset < 0"); + goto exit; + } + _remaining = _env->GetArrayLength(coords_ref) - offset; + if (_remaining < 5) { + _env->ThrowNew(IAEClass, "length - offset < 5"); + goto exit; + } + coords_base = (GLfixed *) + _env->GetPrimitiveArrayCritical(coords_ref, (jboolean *)0); + coords = coords_base + offset; + + glDrawTexxvOES( + (GLfixed *)coords + ); + +exit: + if (coords_base) { + _env->ReleasePrimitiveArrayCritical(coords_ref, coords_base, + JNI_ABORT); + } +} + +/* void glDrawTexxvOES ( const GLfixed *coords ) */ +static void +android_glDrawTexxvOES__Ljava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jobject coords_buf) { + jarray _array = (jarray) 0; + jint _remaining; + GLfixed *coords = (GLfixed *) 0; + + coords = (GLfixed *)getPointer(_env, coords_buf, &_array, &_remaining); + if (_remaining < 5) { + _env->ThrowNew(IAEClass, "remaining() < 5"); + goto exit; + } + glDrawTexxvOES( + (GLfixed *)coords + ); + +exit: + if (_array) { + releasePointer(_env, _array, coords, JNI_FALSE); + } +} + +/* void glLoadPaletteFromModelViewMatrixOES ( void ) */ +static void +android_glLoadPaletteFromModelViewMatrixOES__ + (JNIEnv *_env, jobject _this) { + _env->ThrowNew(UOEClass, + "glLoadPaletteFromModelViewMatrixOES"); +} + +/* void glMatrixIndexPointerOES ( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer ) */ +static void +android_glMatrixIndexPointerOES__IIILjava_nio_Buffer_2 + (JNIEnv *_env, jobject _this, jint size, jint type, jint stride, jobject pointer_buf) { + _env->ThrowNew(UOEClass, + "glMatrixIndexPointerOES"); +} + +/* void glMatrixIndexPointerOES ( GLint size, GLenum type, GLsizei stride, GLint offset ) */ +static void +android_glMatrixIndexPointerOES__IIII + (JNIEnv *_env, jobject _this, jint size, jint type, jint stride, jint offset) { + _env->ThrowNew(UOEClass, + "glMatrixIndexPointerOES"); +} + +/* void glWeightPointerOES ( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer ) */ +static void +android_glWeightPointerOES__IIILjava_nio_Buffer_2 + (JNIEnv *_env, jobject _this, jint size, jint type, jint stride, jobject pointer_buf) { + _env->ThrowNew(UOEClass, + "glWeightPointerOES"); +} + +/* void glWeightPointerOES ( GLint size, GLenum type, GLsizei stride, GLint offset ) */ +static void +android_glWeightPointerOES__IIII + (JNIEnv *_env, jobject _this, jint size, jint type, jint stride, jint offset) { + _env->ThrowNew(UOEClass, + "glWeightPointerOES"); +} + +/* void glBindFramebufferOES ( GLint target, GLint framebuffer ) */ +static void +android_glBindFramebufferOES__II + (JNIEnv *_env, jobject _this, jint target, jint framebuffer) { + _env->ThrowNew(UOEClass, + "glBindFramebufferOES"); +} + +/* void glBindRenderbufferOES ( GLint target, GLint renderbuffer ) */ +static void +android_glBindRenderbufferOES__II + (JNIEnv *_env, jobject _this, jint target, jint renderbuffer) { + _env->ThrowNew(UOEClass, + "glBindRenderbufferOES"); +} + +/* void glBlendEquation ( GLint mode ) */ +static void +android_glBlendEquation__I + (JNIEnv *_env, jobject _this, jint mode) { + _env->ThrowNew(UOEClass, + "glBlendEquation"); +} + +/* void glBlendEquationSeparate ( GLint modeRGB, GLint modeAlpha ) */ +static void +android_glBlendEquationSeparate__II + (JNIEnv *_env, jobject _this, jint modeRGB, jint modeAlpha) { + _env->ThrowNew(UOEClass, + "glBlendEquationSeparate"); +} + +/* void glBlendFuncSeparate ( GLint srcRGB, GLint dstRGB, GLint srcAlpha, GLint dstAlpha ) */ +static void +android_glBlendFuncSeparate__IIII + (JNIEnv *_env, jobject _this, jint srcRGB, jint dstRGB, jint srcAlpha, jint dstAlpha) { + _env->ThrowNew(UOEClass, + "glBlendFuncSeparate"); +} + +/* GLint glCheckFramebufferStatusOES ( GLint target ) */ +static jint +android_glCheckFramebufferStatusOES__I + (JNIEnv *_env, jobject _this, jint target) { + _env->ThrowNew(UOEClass, + "glCheckFramebufferStatusOES"); + return 0; +} + +/* void glDeleteFramebuffersOES ( GLint n, GLint *framebuffers ) */ +static void +android_glDeleteFramebuffersOES__I_3II + (JNIEnv *_env, jobject _this, jint n, jintArray framebuffers_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glDeleteFramebuffersOES"); +} + +/* void glDeleteFramebuffersOES ( GLint n, GLint *framebuffers ) */ +static void +android_glDeleteFramebuffersOES__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint n, jobject framebuffers_buf) { + _env->ThrowNew(UOEClass, + "glDeleteFramebuffersOES"); +} + +/* void glDeleteRenderbuffersOES ( GLint n, GLint *renderbuffers ) */ +static void +android_glDeleteRenderbuffersOES__I_3II + (JNIEnv *_env, jobject _this, jint n, jintArray renderbuffers_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glDeleteRenderbuffersOES"); +} + +/* void glDeleteRenderbuffersOES ( GLint n, GLint *renderbuffers ) */ +static void +android_glDeleteRenderbuffersOES__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint n, jobject renderbuffers_buf) { + _env->ThrowNew(UOEClass, + "glDeleteRenderbuffersOES"); +} + +/* void glFramebufferRenderbufferOES ( GLint target, GLint attachment, GLint renderbuffertarget, GLint renderbuffer ) */ +static void +android_glFramebufferRenderbufferOES__IIII + (JNIEnv *_env, jobject _this, jint target, jint attachment, jint renderbuffertarget, jint renderbuffer) { + _env->ThrowNew(UOEClass, + "glFramebufferRenderbufferOES"); +} + +/* void glFramebufferTexture2DOES ( GLint target, GLint attachment, GLint textarget, GLint texture, GLint level ) */ +static void +android_glFramebufferTexture2DOES__IIIII + (JNIEnv *_env, jobject _this, jint target, jint attachment, jint textarget, jint texture, jint level) { + _env->ThrowNew(UOEClass, + "glFramebufferTexture2DOES"); +} + +/* void glGenerateMipmapOES ( GLint target ) */ +static void +android_glGenerateMipmapOES__I + (JNIEnv *_env, jobject _this, jint target) { + _env->ThrowNew(UOEClass, + "glGenerateMipmapOES"); +} + +/* void glGenFramebuffersOES ( GLint n, GLint *framebuffers ) */ +static void +android_glGenFramebuffersOES__I_3II + (JNIEnv *_env, jobject _this, jint n, jintArray framebuffers_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glGenFramebuffersOES"); +} + +/* void glGenFramebuffersOES ( GLint n, GLint *framebuffers ) */ +static void +android_glGenFramebuffersOES__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint n, jobject framebuffers_buf) { + _env->ThrowNew(UOEClass, + "glGenFramebuffersOES"); +} + +/* void glGenRenderbuffersOES ( GLint n, GLint *renderbuffers ) */ +static void +android_glGenRenderbuffersOES__I_3II + (JNIEnv *_env, jobject _this, jint n, jintArray renderbuffers_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glGenRenderbuffersOES"); +} + +/* void glGenRenderbuffersOES ( GLint n, GLint *renderbuffers ) */ +static void +android_glGenRenderbuffersOES__ILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint n, jobject renderbuffers_buf) { + _env->ThrowNew(UOEClass, + "glGenRenderbuffersOES"); +} + +/* void glGetFramebufferAttachmentParameterivOES ( GLint target, GLint attachment, GLint pname, GLint *params ) */ +static void +android_glGetFramebufferAttachmentParameterivOES__III_3II + (JNIEnv *_env, jobject _this, jint target, jint attachment, jint pname, jintArray params_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glGetFramebufferAttachmentParameterivOES"); +} + +/* void glGetFramebufferAttachmentParameterivOES ( GLint target, GLint attachment, GLint pname, GLint *params ) */ +static void +android_glGetFramebufferAttachmentParameterivOES__IIILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint attachment, jint pname, jobject params_buf) { + _env->ThrowNew(UOEClass, + "glGetFramebufferAttachmentParameterivOES"); +} + +/* void glGetRenderbufferParameterivOES ( GLint target, GLint pname, GLint *params ) */ +static void +android_glGetRenderbufferParameterivOES__II_3II + (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glGetRenderbufferParameterivOES"); +} + +/* void glGetRenderbufferParameterivOES ( GLint target, GLint pname, GLint *params ) */ +static void +android_glGetRenderbufferParameterivOES__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { + _env->ThrowNew(UOEClass, + "glGetRenderbufferParameterivOES"); +} + +/* void glGetTexGenfv ( GLint coord, GLint pname, GLfloat *params ) */ +static void +android_glGetTexGenfv__II_3FI + (JNIEnv *_env, jobject _this, jint coord, jint pname, jfloatArray params_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glGetTexGenfv"); +} + +/* void glGetTexGenfv ( GLint coord, GLint pname, GLfloat *params ) */ +static void +android_glGetTexGenfv__IILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) { + _env->ThrowNew(UOEClass, + "glGetTexGenfv"); +} + +/* void glGetTexGeniv ( GLint coord, GLint pname, GLint *params ) */ +static void +android_glGetTexGeniv__II_3II + (JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glGetTexGeniv"); +} + +/* void glGetTexGeniv ( GLint coord, GLint pname, GLint *params ) */ +static void +android_glGetTexGeniv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) { + _env->ThrowNew(UOEClass, + "glGetTexGeniv"); +} + +/* void glGetTexGenxv ( GLint coord, GLint pname, GLint *params ) */ +static void +android_glGetTexGenxv__II_3II + (JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glGetTexGenxv"); +} + +/* void glGetTexGenxv ( GLint coord, GLint pname, GLint *params ) */ +static void +android_glGetTexGenxv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) { + _env->ThrowNew(UOEClass, + "glGetTexGenxv"); +} + +/* GLboolean glIsFramebufferOES ( GLint framebuffer ) */ +static jboolean +android_glIsFramebufferOES__I + (JNIEnv *_env, jobject _this, jint framebuffer) { + _env->ThrowNew(UOEClass, + "glIsFramebufferOES"); + return JNI_FALSE; +} + +/* GLboolean glIsRenderbufferOES ( GLint renderbuffer ) */ +static jboolean +android_glIsRenderbufferOES__I + (JNIEnv *_env, jobject _this, jint renderbuffer) { + _env->ThrowNew(UOEClass, + "glIsRenderbufferOES"); + return JNI_FALSE; +} + +/* void glRenderbufferStorageOES ( GLint target, GLint internalformat, GLint width, GLint height ) */ +static void +android_glRenderbufferStorageOES__IIII + (JNIEnv *_env, jobject _this, jint target, jint internalformat, jint width, jint height) { + _env->ThrowNew(UOEClass, + "glRenderbufferStorageOES"); +} + +/* void glTexGenf ( GLint coord, GLint pname, GLfloat param ) */ +static void +android_glTexGenf__IIF + (JNIEnv *_env, jobject _this, jint coord, jint pname, jfloat param) { + _env->ThrowNew(UOEClass, + "glTexGenf"); +} + +/* void glTexGenfv ( GLint coord, GLint pname, GLfloat *params ) */ +static void +android_glTexGenfv__II_3FI + (JNIEnv *_env, jobject _this, jint coord, jint pname, jfloatArray params_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glTexGenfv"); +} + +/* void glTexGenfv ( GLint coord, GLint pname, GLfloat *params ) */ +static void +android_glTexGenfv__IILjava_nio_FloatBuffer_2 + (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) { + _env->ThrowNew(UOEClass, + "glTexGenfv"); +} + +/* void glTexGeni ( GLint coord, GLint pname, GLint param ) */ +static void +android_glTexGeni__III + (JNIEnv *_env, jobject _this, jint coord, jint pname, jint param) { + _env->ThrowNew(UOEClass, + "glTexGeni"); +} + +/* void glTexGeniv ( GLint coord, GLint pname, GLint *params ) */ +static void +android_glTexGeniv__II_3II + (JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glTexGeniv"); +} + +/* void glTexGeniv ( GLint coord, GLint pname, GLint *params ) */ +static void +android_glTexGeniv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) { + _env->ThrowNew(UOEClass, + "glTexGeniv"); +} + +/* void glTexGenx ( GLint coord, GLint pname, GLint param ) */ +static void +android_glTexGenx__III + (JNIEnv *_env, jobject _this, jint coord, jint pname, jint param) { + _env->ThrowNew(UOEClass, + "glTexGenx"); +} + +/* void glTexGenxv ( GLint coord, GLint pname, GLint *params ) */ +static void +android_glTexGenxv__II_3II + (JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) { + _env->ThrowNew(UOEClass, + "glTexGenxv"); +} + +/* void glTexGenxv ( GLint coord, GLint pname, GLint *params ) */ +static void +android_glTexGenxv__IILjava_nio_IntBuffer_2 + (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) { + _env->ThrowNew(UOEClass, + "glTexGenxv"); +} + +static const char *classPathName = "com/google/android/gles_jni/GLImpl"; + +static JNINativeMethod methods[] = { +{"_nativeClassInit", "()V", (void*)nativeClassInit }, +{"glActiveTexture", "(I)V", (void *) android_glActiveTexture__I }, +{"glAlphaFunc", "(IF)V", (void *) android_glAlphaFunc__IF }, +{"glAlphaFuncx", "(II)V", (void *) android_glAlphaFuncx__II }, +{"glBindTexture", "(II)V", (void *) android_glBindTexture__II }, +{"glBlendFunc", "(II)V", (void *) android_glBlendFunc__II }, +{"glClear", "(I)V", (void *) android_glClear__I }, +{"glClearColor", "(FFFF)V", (void *) android_glClearColor__FFFF }, +{"glClearColorx", "(IIII)V", (void *) android_glClearColorx__IIII }, +{"glClearDepthf", "(F)V", (void *) android_glClearDepthf__F }, +{"glClearDepthx", "(I)V", (void *) android_glClearDepthx__I }, +{"glClearStencil", "(I)V", (void *) android_glClearStencil__I }, +{"glClientActiveTexture", "(I)V", (void *) android_glClientActiveTexture__I }, +{"glColor4f", "(FFFF)V", (void *) android_glColor4f__FFFF }, +{"glColor4x", "(IIII)V", (void *) android_glColor4x__IIII }, +{"glColorMask", "(ZZZZ)V", (void *) android_glColorMask__ZZZZ }, +{"glColorPointerBounds", "(IIILjava/nio/Buffer;I)V", (void *) android_glColorPointerBounds__IIILjava_nio_Buffer_2I }, +{"glCompressedTexImage2D", "(IIIIIIILjava/nio/Buffer;)V", (void *) android_glCompressedTexImage2D__IIIIIIILjava_nio_Buffer_2 }, +{"glCompressedTexSubImage2D", "(IIIIIIIILjava/nio/Buffer;)V", (void *) android_glCompressedTexSubImage2D__IIIIIIIILjava_nio_Buffer_2 }, +{"glCopyTexImage2D", "(IIIIIIII)V", (void *) android_glCopyTexImage2D__IIIIIIII }, +{"glCopyTexSubImage2D", "(IIIIIIII)V", (void *) android_glCopyTexSubImage2D__IIIIIIII }, +{"glCullFace", "(I)V", (void *) android_glCullFace__I }, +{"glDeleteTextures", "(I[II)V", (void *) android_glDeleteTextures__I_3II }, +{"glDeleteTextures", "(ILjava/nio/IntBuffer;)V", (void *) android_glDeleteTextures__ILjava_nio_IntBuffer_2 }, +{"glDepthFunc", "(I)V", (void *) android_glDepthFunc__I }, +{"glDepthMask", "(Z)V", (void *) android_glDepthMask__Z }, +{"glDepthRangef", "(FF)V", (void *) android_glDepthRangef__FF }, +{"glDepthRangex", "(II)V", (void *) android_glDepthRangex__II }, +{"glDisable", "(I)V", (void *) android_glDisable__I }, +{"glDisableClientState", "(I)V", (void *) android_glDisableClientState__I }, +{"glDrawArrays", "(III)V", (void *) android_glDrawArrays__III }, +{"glDrawElements", "(IIILjava/nio/Buffer;)V", (void *) android_glDrawElements__IIILjava_nio_Buffer_2 }, +{"glEnable", "(I)V", (void *) android_glEnable__I }, +{"glEnableClientState", "(I)V", (void *) android_glEnableClientState__I }, +{"glFinish", "()V", (void *) android_glFinish__ }, +{"glFlush", "()V", (void *) android_glFlush__ }, +{"glFogf", "(IF)V", (void *) android_glFogf__IF }, +{"glFogfv", "(I[FI)V", (void *) android_glFogfv__I_3FI }, +{"glFogfv", "(ILjava/nio/FloatBuffer;)V", (void *) android_glFogfv__ILjava_nio_FloatBuffer_2 }, +{"glFogx", "(II)V", (void *) android_glFogx__II }, +{"glFogxv", "(I[II)V", (void *) android_glFogxv__I_3II }, +{"glFogxv", "(ILjava/nio/IntBuffer;)V", (void *) android_glFogxv__ILjava_nio_IntBuffer_2 }, +{"glFrontFace", "(I)V", (void *) android_glFrontFace__I }, +{"glFrustumf", "(FFFFFF)V", (void *) android_glFrustumf__FFFFFF }, +{"glFrustumx", "(IIIIII)V", (void *) android_glFrustumx__IIIIII }, +{"glGenTextures", "(I[II)V", (void *) android_glGenTextures__I_3II }, +{"glGenTextures", "(ILjava/nio/IntBuffer;)V", (void *) android_glGenTextures__ILjava_nio_IntBuffer_2 }, +{"glGetError", "()I", (void *) android_glGetError__ }, +{"glGetIntegerv", "(I[II)V", (void *) android_glGetIntegerv__I_3II }, +{"glGetIntegerv", "(ILjava/nio/IntBuffer;)V", (void *) android_glGetIntegerv__ILjava_nio_IntBuffer_2 }, +{"_glGetString", "(I)Ljava/lang/String;", (void *) android_glGetString }, +{"glHint", "(II)V", (void *) android_glHint__II }, +{"glLightModelf", "(IF)V", (void *) android_glLightModelf__IF }, +{"glLightModelfv", "(I[FI)V", (void *) android_glLightModelfv__I_3FI }, +{"glLightModelfv", "(ILjava/nio/FloatBuffer;)V", (void *) android_glLightModelfv__ILjava_nio_FloatBuffer_2 }, +{"glLightModelx", "(II)V", (void *) android_glLightModelx__II }, +{"glLightModelxv", "(I[II)V", (void *) android_glLightModelxv__I_3II }, +{"glLightModelxv", "(ILjava/nio/IntBuffer;)V", (void *) android_glLightModelxv__ILjava_nio_IntBuffer_2 }, +{"glLightf", "(IIF)V", (void *) android_glLightf__IIF }, +{"glLightfv", "(II[FI)V", (void *) android_glLightfv__II_3FI }, +{"glLightfv", "(IILjava/nio/FloatBuffer;)V", (void *) android_glLightfv__IILjava_nio_FloatBuffer_2 }, +{"glLightx", "(III)V", (void *) android_glLightx__III }, +{"glLightxv", "(II[II)V", (void *) android_glLightxv__II_3II }, +{"glLightxv", "(IILjava/nio/IntBuffer;)V", (void *) android_glLightxv__IILjava_nio_IntBuffer_2 }, +{"glLineWidth", "(F)V", (void *) android_glLineWidth__F }, +{"glLineWidthx", "(I)V", (void *) android_glLineWidthx__I }, +{"glLoadIdentity", "()V", (void *) android_glLoadIdentity__ }, +{"glLoadMatrixf", "([FI)V", (void *) android_glLoadMatrixf___3FI }, +{"glLoadMatrixf", "(Ljava/nio/FloatBuffer;)V", (void *) android_glLoadMatrixf__Ljava_nio_FloatBuffer_2 }, +{"glLoadMatrixx", "([II)V", (void *) android_glLoadMatrixx___3II }, +{"glLoadMatrixx", "(Ljava/nio/IntBuffer;)V", (void *) android_glLoadMatrixx__Ljava_nio_IntBuffer_2 }, +{"glLogicOp", "(I)V", (void *) android_glLogicOp__I }, +{"glMaterialf", "(IIF)V", (void *) android_glMaterialf__IIF }, +{"glMaterialfv", "(II[FI)V", (void *) android_glMaterialfv__II_3FI }, +{"glMaterialfv", "(IILjava/nio/FloatBuffer;)V", (void *) android_glMaterialfv__IILjava_nio_FloatBuffer_2 }, +{"glMaterialx", "(III)V", (void *) android_glMaterialx__III }, +{"glMaterialxv", "(II[II)V", (void *) android_glMaterialxv__II_3II }, +{"glMaterialxv", "(IILjava/nio/IntBuffer;)V", (void *) android_glMaterialxv__IILjava_nio_IntBuffer_2 }, +{"glMatrixMode", "(I)V", (void *) android_glMatrixMode__I }, +{"glMultMatrixf", "([FI)V", (void *) android_glMultMatrixf___3FI }, +{"glMultMatrixf", "(Ljava/nio/FloatBuffer;)V", (void *) android_glMultMatrixf__Ljava_nio_FloatBuffer_2 }, +{"glMultMatrixx", "([II)V", (void *) android_glMultMatrixx___3II }, +{"glMultMatrixx", "(Ljava/nio/IntBuffer;)V", (void *) android_glMultMatrixx__Ljava_nio_IntBuffer_2 }, +{"glMultiTexCoord4f", "(IFFFF)V", (void *) android_glMultiTexCoord4f__IFFFF }, +{"glMultiTexCoord4x", "(IIIII)V", (void *) android_glMultiTexCoord4x__IIIII }, +{"glNormal3f", "(FFF)V", (void *) android_glNormal3f__FFF }, +{"glNormal3x", "(III)V", (void *) android_glNormal3x__III }, +{"glNormalPointerBounds", "(IILjava/nio/Buffer;I)V", (void *) android_glNormalPointerBounds__IILjava_nio_Buffer_2I }, +{"glOrthof", "(FFFFFF)V", (void *) android_glOrthof__FFFFFF }, +{"glOrthox", "(IIIIII)V", (void *) android_glOrthox__IIIIII }, +{"glPixelStorei", "(II)V", (void *) android_glPixelStorei__II }, +{"glPointSize", "(F)V", (void *) android_glPointSize__F }, +{"glPointSizex", "(I)V", (void *) android_glPointSizex__I }, +{"glPolygonOffset", "(FF)V", (void *) android_glPolygonOffset__FF }, +{"glPolygonOffsetx", "(II)V", (void *) android_glPolygonOffsetx__II }, +{"glPopMatrix", "()V", (void *) android_glPopMatrix__ }, +{"glPushMatrix", "()V", (void *) android_glPushMatrix__ }, +{"glReadPixels", "(IIIIIILjava/nio/Buffer;)V", (void *) android_glReadPixels__IIIIIILjava_nio_Buffer_2 }, +{"glRotatef", "(FFFF)V", (void *) android_glRotatef__FFFF }, +{"glRotatex", "(IIII)V", (void *) android_glRotatex__IIII }, +{"glSampleCoverage", "(FZ)V", (void *) android_glSampleCoverage__FZ }, +{"glSampleCoveragex", "(IZ)V", (void *) android_glSampleCoveragex__IZ }, +{"glScalef", "(FFF)V", (void *) android_glScalef__FFF }, +{"glScalex", "(III)V", (void *) android_glScalex__III }, +{"glScissor", "(IIII)V", (void *) android_glScissor__IIII }, +{"glShadeModel", "(I)V", (void *) android_glShadeModel__I }, +{"glStencilFunc", "(III)V", (void *) android_glStencilFunc__III }, +{"glStencilMask", "(I)V", (void *) android_glStencilMask__I }, +{"glStencilOp", "(III)V", (void *) android_glStencilOp__III }, +{"glTexCoordPointerBounds", "(IIILjava/nio/Buffer;I)V", (void *) android_glTexCoordPointerBounds__IIILjava_nio_Buffer_2I }, +{"glTexEnvf", "(IIF)V", (void *) android_glTexEnvf__IIF }, +{"glTexEnvfv", "(II[FI)V", (void *) android_glTexEnvfv__II_3FI }, +{"glTexEnvfv", "(IILjava/nio/FloatBuffer;)V", (void *) android_glTexEnvfv__IILjava_nio_FloatBuffer_2 }, +{"glTexEnvx", "(III)V", (void *) android_glTexEnvx__III }, +{"glTexEnvxv", "(II[II)V", (void *) android_glTexEnvxv__II_3II }, +{"glTexEnvxv", "(IILjava/nio/IntBuffer;)V", (void *) android_glTexEnvxv__IILjava_nio_IntBuffer_2 }, +{"glTexImage2D", "(IIIIIIIILjava/nio/Buffer;)V", (void *) android_glTexImage2D__IIIIIIIILjava_nio_Buffer_2 }, +{"glTexParameterf", "(IIF)V", (void *) android_glTexParameterf__IIF }, +{"glTexParameterx", "(III)V", (void *) android_glTexParameterx__III }, +{"glTexSubImage2D", "(IIIIIIIILjava/nio/Buffer;)V", (void *) android_glTexSubImage2D__IIIIIIIILjava_nio_Buffer_2 }, +{"glTranslatef", "(FFF)V", (void *) android_glTranslatef__FFF }, +{"glTranslatex", "(III)V", (void *) android_glTranslatex__III }, +{"glVertexPointerBounds", "(IIILjava/nio/Buffer;I)V", (void *) android_glVertexPointerBounds__IIILjava_nio_Buffer_2I }, +{"glViewport", "(IIII)V", (void *) android_glViewport__IIII }, +{"glQueryMatrixxOES", "([II[II)I", (void *) android_glQueryMatrixxOES___3II_3II }, +{"glQueryMatrixxOES", "(Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;)I", (void *) android_glQueryMatrixxOES__Ljava_nio_IntBuffer_2Ljava_nio_IntBuffer_2 }, +{"glBindBuffer", "(II)V", (void *) android_glBindBuffer__II }, +{"glBufferData", "(IILjava/nio/Buffer;I)V", (void *) android_glBufferData__IILjava_nio_Buffer_2I }, +{"glBufferSubData", "(IIILjava/nio/Buffer;)V", (void *) android_glBufferSubData__IIILjava_nio_Buffer_2 }, +{"glClipPlanef", "(I[FI)V", (void *) android_glClipPlanef__I_3FI }, +{"glClipPlanef", "(ILjava/nio/FloatBuffer;)V", (void *) android_glClipPlanef__ILjava_nio_FloatBuffer_2 }, +{"glClipPlanex", "(I[II)V", (void *) android_glClipPlanex__I_3II }, +{"glClipPlanex", "(ILjava/nio/IntBuffer;)V", (void *) android_glClipPlanex__ILjava_nio_IntBuffer_2 }, +{"glColor4ub", "(BBBB)V", (void *) android_glColor4ub__BBBB }, +{"glColorPointer", "(IIII)V", (void *) android_glColorPointer__IIII }, +{"glDeleteBuffers", "(I[II)V", (void *) android_glDeleteBuffers__I_3II }, +{"glDeleteBuffers", "(ILjava/nio/IntBuffer;)V", (void *) android_glDeleteBuffers__ILjava_nio_IntBuffer_2 }, +{"glDrawElements", "(IIII)V", (void *) android_glDrawElements__IIII }, +{"glGenBuffers", "(I[II)V", (void *) android_glGenBuffers__I_3II }, +{"glGenBuffers", "(ILjava/nio/IntBuffer;)V", (void *) android_glGenBuffers__ILjava_nio_IntBuffer_2 }, +{"glGetBooleanv", "(I[ZI)V", (void *) android_glGetBooleanv__I_3ZI }, +{"glGetBooleanv", "(ILjava/nio/IntBuffer;)V", (void *) android_glGetBooleanv__ILjava_nio_IntBuffer_2 }, +{"glGetBufferParameteriv", "(II[II)V", (void *) android_glGetBufferParameteriv__II_3II }, +{"glGetBufferParameteriv", "(IILjava/nio/IntBuffer;)V", (void *) android_glGetBufferParameteriv__IILjava_nio_IntBuffer_2 }, +{"glGetClipPlanef", "(I[FI)V", (void *) android_glGetClipPlanef__I_3FI }, +{"glGetClipPlanef", "(ILjava/nio/FloatBuffer;)V", (void *) android_glGetClipPlanef__ILjava_nio_FloatBuffer_2 }, +{"glGetClipPlanex", "(I[II)V", (void *) android_glGetClipPlanex__I_3II }, +{"glGetClipPlanex", "(ILjava/nio/IntBuffer;)V", (void *) android_glGetClipPlanex__ILjava_nio_IntBuffer_2 }, +{"glGetFixedv", "(I[II)V", (void *) android_glGetFixedv__I_3II }, +{"glGetFixedv", "(ILjava/nio/IntBuffer;)V", (void *) android_glGetFixedv__ILjava_nio_IntBuffer_2 }, +{"glGetFloatv", "(I[FI)V", (void *) android_glGetFloatv__I_3FI }, +{"glGetFloatv", "(ILjava/nio/FloatBuffer;)V", (void *) android_glGetFloatv__ILjava_nio_FloatBuffer_2 }, +{"glGetLightfv", "(II[FI)V", (void *) android_glGetLightfv__II_3FI }, +{"glGetLightfv", "(IILjava/nio/FloatBuffer;)V", (void *) android_glGetLightfv__IILjava_nio_FloatBuffer_2 }, +{"glGetLightxv", "(II[II)V", (void *) android_glGetLightxv__II_3II }, +{"glGetLightxv", "(IILjava/nio/IntBuffer;)V", (void *) android_glGetLightxv__IILjava_nio_IntBuffer_2 }, +{"glGetMaterialfv", "(II[FI)V", (void *) android_glGetMaterialfv__II_3FI }, +{"glGetMaterialfv", "(IILjava/nio/FloatBuffer;)V", (void *) android_glGetMaterialfv__IILjava_nio_FloatBuffer_2 }, +{"glGetMaterialxv", "(II[II)V", (void *) android_glGetMaterialxv__II_3II }, +{"glGetMaterialxv", "(IILjava/nio/IntBuffer;)V", (void *) android_glGetMaterialxv__IILjava_nio_IntBuffer_2 }, +{"glGetTexEnviv", "(II[II)V", (void *) android_glGetTexEnviv__II_3II }, +{"glGetTexEnviv", "(IILjava/nio/IntBuffer;)V", (void *) android_glGetTexEnviv__IILjava_nio_IntBuffer_2 }, +{"glGetTexEnvxv", "(II[II)V", (void *) android_glGetTexEnvxv__II_3II }, +{"glGetTexEnvxv", "(IILjava/nio/IntBuffer;)V", (void *) android_glGetTexEnvxv__IILjava_nio_IntBuffer_2 }, +{"glGetTexParameterfv", "(II[FI)V", (void *) android_glGetTexParameterfv__II_3FI }, +{"glGetTexParameterfv", "(IILjava/nio/FloatBuffer;)V", (void *) android_glGetTexParameterfv__IILjava_nio_FloatBuffer_2 }, +{"glGetTexParameteriv", "(II[II)V", (void *) android_glGetTexParameteriv__II_3II }, +{"glGetTexParameteriv", "(IILjava/nio/IntBuffer;)V", (void *) android_glGetTexParameteriv__IILjava_nio_IntBuffer_2 }, +{"glGetTexParameterxv", "(II[II)V", (void *) android_glGetTexParameterxv__II_3II }, +{"glGetTexParameterxv", "(IILjava/nio/IntBuffer;)V", (void *) android_glGetTexParameterxv__IILjava_nio_IntBuffer_2 }, +{"glIsBuffer", "(I)Z", (void *) android_glIsBuffer__I }, +{"glIsEnabled", "(I)Z", (void *) android_glIsEnabled__I }, +{"glIsTexture", "(I)Z", (void *) android_glIsTexture__I }, +{"glNormalPointer", "(III)V", (void *) android_glNormalPointer__III }, +{"glPointParameterf", "(IF)V", (void *) android_glPointParameterf__IF }, +{"glPointParameterfv", "(I[FI)V", (void *) android_glPointParameterfv__I_3FI }, +{"glPointParameterfv", "(ILjava/nio/FloatBuffer;)V", (void *) android_glPointParameterfv__ILjava_nio_FloatBuffer_2 }, +{"glPointParameterx", "(II)V", (void *) android_glPointParameterx__II }, +{"glPointParameterxv", "(I[II)V", (void *) android_glPointParameterxv__I_3II }, +{"glPointParameterxv", "(ILjava/nio/IntBuffer;)V", (void *) android_glPointParameterxv__ILjava_nio_IntBuffer_2 }, +{"glPointSizePointerOES", "(IILjava/nio/Buffer;)V", (void *) android_glPointSizePointerOES__IILjava_nio_Buffer_2 }, +{"glTexCoordPointer", "(IIII)V", (void *) android_glTexCoordPointer__IIII }, +{"glTexEnvi", "(III)V", (void *) android_glTexEnvi__III }, +{"glTexEnviv", "(II[II)V", (void *) android_glTexEnviv__II_3II }, +{"glTexEnviv", "(IILjava/nio/IntBuffer;)V", (void *) android_glTexEnviv__IILjava_nio_IntBuffer_2 }, +{"glTexParameterfv", "(II[FI)V", (void *) android_glTexParameterfv__II_3FI }, +{"glTexParameterfv", "(IILjava/nio/FloatBuffer;)V", (void *) android_glTexParameterfv__IILjava_nio_FloatBuffer_2 }, +{"glTexParameteri", "(III)V", (void *) android_glTexParameteri__III }, +{"glTexParameteriv", "(II[II)V", (void *) android_glTexParameteriv__II_3II }, +{"glTexParameteriv", "(IILjava/nio/IntBuffer;)V", (void *) android_glTexParameteriv__IILjava_nio_IntBuffer_2 }, +{"glTexParameterxv", "(II[II)V", (void *) android_glTexParameterxv__II_3II }, +{"glTexParameterxv", "(IILjava/nio/IntBuffer;)V", (void *) android_glTexParameterxv__IILjava_nio_IntBuffer_2 }, +{"glVertexPointer", "(IIII)V", (void *) android_glVertexPointer__IIII }, +{"glCurrentPaletteMatrixOES", "(I)V", (void *) android_glCurrentPaletteMatrixOES__I }, +{"glDrawTexfOES", "(FFFFF)V", (void *) android_glDrawTexfOES__FFFFF }, +{"glDrawTexfvOES", "([FI)V", (void *) android_glDrawTexfvOES___3FI }, +{"glDrawTexfvOES", "(Ljava/nio/FloatBuffer;)V", (void *) android_glDrawTexfvOES__Ljava_nio_FloatBuffer_2 }, +{"glDrawTexiOES", "(IIIII)V", (void *) android_glDrawTexiOES__IIIII }, +{"glDrawTexivOES", "([II)V", (void *) android_glDrawTexivOES___3II }, +{"glDrawTexivOES", "(Ljava/nio/IntBuffer;)V", (void *) android_glDrawTexivOES__Ljava_nio_IntBuffer_2 }, +{"glDrawTexsOES", "(SSSSS)V", (void *) android_glDrawTexsOES__SSSSS }, +{"glDrawTexsvOES", "([SI)V", (void *) android_glDrawTexsvOES___3SI }, +{"glDrawTexsvOES", "(Ljava/nio/ShortBuffer;)V", (void *) android_glDrawTexsvOES__Ljava_nio_ShortBuffer_2 }, +{"glDrawTexxOES", "(IIIII)V", (void *) android_glDrawTexxOES__IIIII }, +{"glDrawTexxvOES", "([II)V", (void *) android_glDrawTexxvOES___3II }, +{"glDrawTexxvOES", "(Ljava/nio/IntBuffer;)V", (void *) android_glDrawTexxvOES__Ljava_nio_IntBuffer_2 }, +{"glLoadPaletteFromModelViewMatrixOES", "()V", (void *) android_glLoadPaletteFromModelViewMatrixOES__ }, +{"glMatrixIndexPointerOES", "(IIILjava/nio/Buffer;)V", (void *) android_glMatrixIndexPointerOES__IIILjava_nio_Buffer_2 }, +{"glMatrixIndexPointerOES", "(IIII)V", (void *) android_glMatrixIndexPointerOES__IIII }, +{"glWeightPointerOES", "(IIILjava/nio/Buffer;)V", (void *) android_glWeightPointerOES__IIILjava_nio_Buffer_2 }, +{"glWeightPointerOES", "(IIII)V", (void *) android_glWeightPointerOES__IIII }, +{"glBindFramebufferOES", "(II)V", (void *) android_glBindFramebufferOES__II }, +{"glBindRenderbufferOES", "(II)V", (void *) android_glBindRenderbufferOES__II }, +{"glBlendEquation", "(I)V", (void *) android_glBlendEquation__I }, +{"glBlendEquationSeparate", "(II)V", (void *) android_glBlendEquationSeparate__II }, +{"glBlendFuncSeparate", "(IIII)V", (void *) android_glBlendFuncSeparate__IIII }, +{"glCheckFramebufferStatusOES", "(I)I", (void *) android_glCheckFramebufferStatusOES__I }, +{"glDeleteFramebuffersOES", "(I[II)V", (void *) android_glDeleteFramebuffersOES__I_3II }, +{"glDeleteFramebuffersOES", "(ILjava/nio/IntBuffer;)V", (void *) android_glDeleteFramebuffersOES__ILjava_nio_IntBuffer_2 }, +{"glDeleteRenderbuffersOES", "(I[II)V", (void *) android_glDeleteRenderbuffersOES__I_3II }, +{"glDeleteRenderbuffersOES", "(ILjava/nio/IntBuffer;)V", (void *) android_glDeleteRenderbuffersOES__ILjava_nio_IntBuffer_2 }, +{"glFramebufferRenderbufferOES", "(IIII)V", (void *) android_glFramebufferRenderbufferOES__IIII }, +{"glFramebufferTexture2DOES", "(IIIII)V", (void *) android_glFramebufferTexture2DOES__IIIII }, +{"glGenerateMipmapOES", "(I)V", (void *) android_glGenerateMipmapOES__I }, +{"glGenFramebuffersOES", "(I[II)V", (void *) android_glGenFramebuffersOES__I_3II }, +{"glGenFramebuffersOES", "(ILjava/nio/IntBuffer;)V", (void *) android_glGenFramebuffersOES__ILjava_nio_IntBuffer_2 }, +{"glGenRenderbuffersOES", "(I[II)V", (void *) android_glGenRenderbuffersOES__I_3II }, +{"glGenRenderbuffersOES", "(ILjava/nio/IntBuffer;)V", (void *) android_glGenRenderbuffersOES__ILjava_nio_IntBuffer_2 }, +{"glGetFramebufferAttachmentParameterivOES", "(III[II)V", (void *) android_glGetFramebufferAttachmentParameterivOES__III_3II }, +{"glGetFramebufferAttachmentParameterivOES", "(IIILjava/nio/IntBuffer;)V", (void *) android_glGetFramebufferAttachmentParameterivOES__IIILjava_nio_IntBuffer_2 }, +{"glGetRenderbufferParameterivOES", "(II[II)V", (void *) android_glGetRenderbufferParameterivOES__II_3II }, +{"glGetRenderbufferParameterivOES", "(IILjava/nio/IntBuffer;)V", (void *) android_glGetRenderbufferParameterivOES__IILjava_nio_IntBuffer_2 }, +{"glGetTexGenfv", "(II[FI)V", (void *) android_glGetTexGenfv__II_3FI }, +{"glGetTexGenfv", "(IILjava/nio/FloatBuffer;)V", (void *) android_glGetTexGenfv__IILjava_nio_FloatBuffer_2 }, +{"glGetTexGeniv", "(II[II)V", (void *) android_glGetTexGeniv__II_3II }, +{"glGetTexGeniv", "(IILjava/nio/IntBuffer;)V", (void *) android_glGetTexGeniv__IILjava_nio_IntBuffer_2 }, +{"glGetTexGenxv", "(II[II)V", (void *) android_glGetTexGenxv__II_3II }, +{"glGetTexGenxv", "(IILjava/nio/IntBuffer;)V", (void *) android_glGetTexGenxv__IILjava_nio_IntBuffer_2 }, +{"glIsFramebufferOES", "(I)Z", (void *) android_glIsFramebufferOES__I }, +{"glIsRenderbufferOES", "(I)Z", (void *) android_glIsRenderbufferOES__I }, +{"glRenderbufferStorageOES", "(IIII)V", (void *) android_glRenderbufferStorageOES__IIII }, +{"glTexGenf", "(IIF)V", (void *) android_glTexGenf__IIF }, +{"glTexGenfv", "(II[FI)V", (void *) android_glTexGenfv__II_3FI }, +{"glTexGenfv", "(IILjava/nio/FloatBuffer;)V", (void *) android_glTexGenfv__IILjava_nio_FloatBuffer_2 }, +{"glTexGeni", "(III)V", (void *) android_glTexGeni__III }, +{"glTexGeniv", "(II[II)V", (void *) android_glTexGeniv__II_3II }, +{"glTexGeniv", "(IILjava/nio/IntBuffer;)V", (void *) android_glTexGeniv__IILjava_nio_IntBuffer_2 }, +{"glTexGenx", "(III)V", (void *) android_glTexGenx__III }, +{"glTexGenxv", "(II[II)V", (void *) android_glTexGenxv__II_3II }, +{"glTexGenxv", "(IILjava/nio/IntBuffer;)V", (void *) android_glTexGenxv__IILjava_nio_IntBuffer_2 }, +}; + +int register_com_google_android_gles_jni_GLImpl(JNIEnv *_env) +{ + int err; + err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods)); + return err; +} diff --git a/core/jni/server/Android.mk b/core/jni/server/Android.mk new file mode 100644 index 0000000..d108330 --- /dev/null +++ b/core/jni/server/Android.mk @@ -0,0 +1,37 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + com_android_server_AlarmManagerService.cpp \ + com_android_server_BatteryService.cpp \ + com_android_server_HardwareService.cpp \ + com_android_server_KeyInputQueue.cpp \ + com_android_server_SensorService.cpp \ + com_android_server_SystemServer.cpp \ + onload.cpp + +LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libhardware \ + libnativehelper \ + libsystem_server \ + libutils \ + libui + +ifeq ($(TARGET_OS),linux) +ifeq ($(TARGET_ARCH),x86) +LOCAL_LDLIBS += -lpthread -ldl -lrt +endif +endif + +ifeq ($(WITH_MALLOC_LEAK_CHECK),true) + LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK +endif + +LOCAL_MODULE:= libandroid_servers + +include $(BUILD_SHARED_LIBRARY) + diff --git a/core/jni/server/com_android_server_AlarmManagerService.cpp b/core/jni/server/com_android_server_AlarmManagerService.cpp new file mode 100644 index 0000000..a81a0ff --- /dev/null +++ b/core/jni/server/com_android_server_AlarmManagerService.cpp @@ -0,0 +1,120 @@ +/* //device/libs/android_runtime/android_server_AlarmManagerService.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "AlarmManagerService" + +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> + +#if HAVE_ANDROID_OS +#include <linux/ioctl.h> +#include <linux/android_alarm.h> +#endif + +#define ONE_NANOSECOND 1000000000LL +#define NANOSECONDS_TO_SECONDS(x) (x / ONE_NANOSECOND) +#define SECONDS_TO_NANOSECONDS(x) (x * ONE_NANOSECOND) + +namespace android { + +static jint android_server_AlarmManagerService_init(JNIEnv* env, jobject obj) +{ +#if HAVE_ANDROID_OS + return open("/dev/alarm", O_RDWR); +#else + return -1; +#endif +} + +static void android_server_AlarmManagerService_close(JNIEnv* env, jobject obj, jint fd) +{ +#if HAVE_ANDROID_OS + close(fd); +#endif +} + +static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong nanoseconds) +{ +#if HAVE_ANDROID_OS + struct timespec ts; + ts.tv_sec = NANOSECONDS_TO_SECONDS(nanoseconds); + ts.tv_nsec = nanoseconds - SECONDS_TO_NANOSECONDS(ts.tv_sec); + + int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts); + if (result < 0) + { + LOGE("Unable to set alarm to %lld: %s\n", nanoseconds, strerror(errno)); + } +#endif +} + +static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv* env, jobject obj, jint fd) +{ +#if HAVE_ANDROID_OS + int result = 0; + + do + { + result = ioctl(fd, ANDROID_ALARM_WAIT); + } while (result < 0 && errno == EINTR); + + if (result < 0) + { + LOGE("Unable to wait on alarm: %s\n", strerror(errno)); + return 0; + } + + return result; +#endif +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"init", "()I", (void*)android_server_AlarmManagerService_init}, + {"close", "(I)V", (void*)android_server_AlarmManagerService_close}, + {"set", "(IIJ)V", (void*)android_server_AlarmManagerService_set}, + {"waitForAlarm", "(I)I", (void*)android_server_AlarmManagerService_waitForAlarm}, +}; + +int register_android_server_AlarmManagerService(JNIEnv* env) +{ + jclass clazz = env->FindClass("com/android/server/AlarmManagerService"); + + if (clazz == NULL) + { + LOGE("Can't find com/android/server/AlarmManagerService"); + return -1; + } + + return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService", + sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/server/com_android_server_BatteryService.cpp b/core/jni/server/com_android_server_BatteryService.cpp new file mode 100644 index 0000000..6636a97 --- /dev/null +++ b/core/jni/server/com_android_server_BatteryService.cpp @@ -0,0 +1,274 @@ +/* + * 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_TAG "BatteryService" + +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> + +#if HAVE_ANDROID_OS +#include <linux/ioctl.h> +#endif + +namespace android { + +#define AC_ONLINE_PATH "/sys/class/power_supply/ac/online" +#define USB_ONLINE_PATH "/sys/class/power_supply/usb/online" +#define BATTERY_STATUS_PATH "/sys/class/power_supply/battery/status" +#define BATTERY_HEALTH_PATH "/sys/class/power_supply/battery/health" +#define BATTERY_PRESENT_PATH "/sys/class/power_supply/battery/present" +#define BATTERY_CAPACITY_PATH "/sys/class/power_supply/battery/capacity" +#define BATTERY_VOLTAGE_PATH "/sys/class/power_supply/battery/batt_vol" +#define BATTERY_TEMPERATURE_PATH "/sys/class/power_supply/battery/batt_temp" +#define BATTERY_TECHNOLOGY_PATH "/sys/class/power_supply/battery/technology" + +struct FieldIds { + // members + jfieldID mAcOnline; + jfieldID mUsbOnline; + jfieldID mBatteryStatus; + jfieldID mBatteryHealth; + jfieldID mBatteryPresent; + jfieldID mBatteryLevel; + jfieldID mBatteryVoltage; + jfieldID mBatteryTemperature; + jfieldID mBatteryTechnology; +}; +static FieldIds gFieldIds; + +struct BatteryManagerConstants { + jint statusUnknown; + jint statusCharging; + jint statusDischarging; + jint statusNotCharging; + jint statusFull; + jint healthUnknown; + jint healthGood; + jint healthOverheat; + jint healthDead; + jint healthOverVoltage; + jint healthUnspecifiedFailure; +}; +static BatteryManagerConstants gConstants; + +static jint getBatteryStatus(const char* status) +{ + switch (status[0]) { + case 'C': return gConstants.statusCharging; // Charging + case 'D': return gConstants.statusDischarging; // Discharging + case 'F': return gConstants.statusFull; // Not charging + case 'N': return gConstants.statusNotCharging; // Full + case 'U': return gConstants.statusUnknown; // Unknown + + default: { + LOGW("Unknown battery status '%s'", status); + return gConstants.statusUnknown; + } + } +} + +static jint getBatteryHealth(const char* status) +{ + switch (status[0]) { + case 'D': return gConstants.healthDead; // Dead + case 'G': return gConstants.healthGood; // Good + case 'O': { + if (strcmp(status, "Overheat") == 0) { + return gConstants.healthOverheat; + } else if (strcmp(status, "Over voltage") == 0) { + return gConstants.healthOverVoltage; + } + LOGW("Unknown battery health[1] '%s'", status); + return gConstants.healthUnknown; + } + + case 'U': { + if (strcmp(status, "Unspecified failure") == 0) { + return gConstants.healthUnspecifiedFailure; + } else if (strcmp(status, "Unknown") == 0) { + return gConstants.healthUnknown; + } + // fall through + } + + default: { + LOGW("Unknown battery health[2] '%s'", status); + return gConstants.healthUnknown; + } + } +} + +static int readFromFile(const char* path, char* buf, size_t size) +{ + int fd = open(path, O_RDONLY, 0); + if (fd == -1) { + LOGE("Could not open '%s'", path); + return -1; + } + + size_t count = read(fd, buf, size); + if (count > 0) { + count = (count < size) ? count : size - 1; + while (count > 0 && buf[count-1] == '\n') count--; + buf[count] = '\0'; + } else { + buf[0] = '\0'; + } + + close(fd); + return count; +} + +static void setBooleanField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID) +{ + const int SIZE = 16; + char buf[SIZE]; + + jboolean value = false; + if (readFromFile(path, buf, SIZE) > 0) { + if (buf[0] == '1') { + value = true; + } + } + env->SetBooleanField(obj, fieldID, value); +} + +static void setIntField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID) +{ + const int SIZE = 128; + char buf[SIZE]; + + jint value = 0; + if (readFromFile(path, buf, SIZE) > 0) { + value = atoi(buf); + } + env->SetIntField(obj, fieldID, value); +} + +static void android_server_BatteryService_update(JNIEnv* env, jobject obj) +{ + setBooleanField(env, obj, AC_ONLINE_PATH, gFieldIds.mAcOnline); + setBooleanField(env, obj, USB_ONLINE_PATH, gFieldIds.mUsbOnline); + setBooleanField(env, obj, BATTERY_PRESENT_PATH, gFieldIds.mBatteryPresent); + + setIntField(env, obj, BATTERY_CAPACITY_PATH, gFieldIds.mBatteryLevel); + setIntField(env, obj, BATTERY_VOLTAGE_PATH, gFieldIds.mBatteryVoltage); + setIntField(env, obj, BATTERY_TEMPERATURE_PATH, gFieldIds.mBatteryTemperature); + + const int SIZE = 128; + char buf[SIZE]; + + if (readFromFile(BATTERY_STATUS_PATH, buf, SIZE) > 0) + env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf)); + + if (readFromFile(BATTERY_HEALTH_PATH, buf, SIZE) > 0) + env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf)); + + if (readFromFile(BATTERY_TECHNOLOGY_PATH, buf, SIZE) > 0) + env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF(buf)); +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"native_update", "()V", (void*)android_server_BatteryService_update}, +}; + +int register_android_server_BatteryService(JNIEnv* env) +{ + jclass clazz = env->FindClass("com/android/server/BatteryService"); + + if (clazz == NULL) { + LOGE("Can't find com/android/server/BatteryService"); + return -1; + } + + gFieldIds.mAcOnline = env->GetFieldID(clazz, "mAcOnline", "Z"); + gFieldIds.mUsbOnline = env->GetFieldID(clazz, "mUsbOnline", "Z"); + gFieldIds.mBatteryStatus = env->GetFieldID(clazz, "mBatteryStatus", "I"); + gFieldIds.mBatteryHealth = env->GetFieldID(clazz, "mBatteryHealth", "I"); + gFieldIds.mBatteryPresent = env->GetFieldID(clazz, "mBatteryPresent", "Z"); + gFieldIds.mBatteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I"); + gFieldIds.mBatteryTechnology = env->GetFieldID(clazz, "mBatteryTechnology", "Ljava/lang/String;"); + gFieldIds.mBatteryVoltage = env->GetFieldID(clazz, "mBatteryVoltage", "I"); + gFieldIds.mBatteryTemperature = env->GetFieldID(clazz, "mBatteryTemperature", "I"); + + LOG_FATAL_IF(gFieldIds.mAcOnline == NULL, "Unable to find BatteryService.AC_ONLINE_PATH"); + LOG_FATAL_IF(gFieldIds.mUsbOnline == NULL, "Unable to find BatteryService.USB_ONLINE_PATH"); + LOG_FATAL_IF(gFieldIds.mBatteryStatus == NULL, "Unable to find BatteryService.BATTERY_STATUS_PATH"); + LOG_FATAL_IF(gFieldIds.mBatteryHealth == NULL, "Unable to find BatteryService.BATTERY_HEALTH_PATH"); + LOG_FATAL_IF(gFieldIds.mBatteryPresent == NULL, "Unable to find BatteryService.BATTERY_PRESENT_PATH"); + LOG_FATAL_IF(gFieldIds.mBatteryLevel == NULL, "Unable to find BatteryService.BATTERY_CAPACITY_PATH"); + LOG_FATAL_IF(gFieldIds.mBatteryVoltage == NULL, "Unable to find BatteryService.BATTERY_VOLTAGE_PATH"); + LOG_FATAL_IF(gFieldIds.mBatteryTemperature == NULL, "Unable to find BatteryService.BATTERY_TEMPERATURE_PATH"); + LOG_FATAL_IF(gFieldIds.mBatteryTechnology == NULL, "Unable to find BatteryService.BATTERY_TECHNOLOGY_PATH"); + + clazz = env->FindClass("android/os/BatteryManager"); + + if (clazz == NULL) { + LOGE("Can't find android/os/BatteryManager"); + return -1; + } + + gConstants.statusUnknown = env->GetStaticIntField(clazz, + env->GetStaticFieldID(clazz, "BATTERY_STATUS_UNKNOWN", "I")); + + gConstants.statusCharging = env->GetStaticIntField(clazz, + env->GetStaticFieldID(clazz, "BATTERY_STATUS_CHARGING", "I")); + + gConstants.statusDischarging = env->GetStaticIntField(clazz, + env->GetStaticFieldID(clazz, "BATTERY_STATUS_DISCHARGING", "I")); + + gConstants.statusNotCharging = env->GetStaticIntField(clazz, + env->GetStaticFieldID(clazz, "BATTERY_STATUS_NOT_CHARGING", "I")); + + gConstants.statusFull = env->GetStaticIntField(clazz, + env->GetStaticFieldID(clazz, "BATTERY_STATUS_FULL", "I")); + + gConstants.healthUnknown = env->GetStaticIntField(clazz, + env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNKNOWN", "I")); + + gConstants.healthGood = env->GetStaticIntField(clazz, + env->GetStaticFieldID(clazz, "BATTERY_HEALTH_GOOD", "I")); + + gConstants.healthOverheat = env->GetStaticIntField(clazz, + env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVERHEAT", "I")); + + gConstants.healthDead = env->GetStaticIntField(clazz, + env->GetStaticFieldID(clazz, "BATTERY_HEALTH_DEAD", "I")); + + gConstants.healthOverVoltage = env->GetStaticIntField(clazz, + env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVER_VOLTAGE", "I")); + + gConstants.healthUnspecifiedFailure = env->GetStaticIntField(clazz, + env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNSPECIFIED_FAILURE", "I")); + + return jniRegisterNativeMethods(env, "com/android/server/BatteryService", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/core/jni/server/com_android_server_HardwareService.cpp b/core/jni/server/com_android_server_HardwareService.cpp new file mode 100644 index 0000000..479e57d --- /dev/null +++ b/core/jni/server/com_android_server_HardwareService.cpp @@ -0,0 +1,54 @@ +/* //device/libs/android_runtime/android_os_Vibrator.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "Vibrator" + +#include "jni.h" +#include "JNIHelp.h" +#include <stdio.h> +#include "android_runtime/AndroidRuntime.h" +#include <utils/misc.h> +#include <utils/Log.h> +#include <hardware/vibrator.h> + +namespace android +{ + +static void on(JNIEnv *env, jobject clazz) +{ + // LOGI("on\n"); + vibrator_on(); +} + +static void off(JNIEnv *env, jobject clazz) +{ + // LOGI("off\n"); + vibrator_off(); +} + +static JNINativeMethod method_table[] = { + { "on", "()V", (void*)on }, + { "off", "()V", (void*)off } +}; + +int register_android_os_Vibrator(JNIEnv *env) +{ + return jniRegisterNativeMethods(env, "com/android/server/HardwareService", + method_table, NELEM(method_table)); +} + +}; diff --git a/core/jni/server/com_android_server_KeyInputQueue.cpp b/core/jni/server/com_android_server_KeyInputQueue.cpp new file mode 100644 index 0000000..4e9ffb1 --- /dev/null +++ b/core/jni/server/com_android_server_KeyInputQueue.cpp @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Input" + +#include "jni.h" +#include "JNIHelp.h" +#include <utils/misc.h> +#include <utils/Log.h> + +#include <ui/EventHub.h> +#include <utils/threads.h> + +#include <stdio.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +static struct input_offsets_t +{ + jfieldID mMinValue; + jfieldID mMaxValue; + jfieldID mFlat; + jfieldID mFuzz; + + jfieldID mDeviceId; + jfieldID mType; + jfieldID mScancode; + jfieldID mKeycode; + jfieldID mFlags; + jfieldID mValue; + jfieldID mWhen; +} gInputOffsets; + +// ---------------------------------------------------------------------------- + +static Mutex gLock; +static sp<EventHub> gHub; + +static jboolean +android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, + jobject event) +{ + gLock.lock(); + sp<EventHub> hub = gHub; + if (hub == NULL) { + hub = new EventHub; + gHub = hub; + } + gLock.unlock(); + + int32_t deviceId; + int32_t type; + int32_t scancode, keycode; + uint32_t flags; + int32_t value; + nsecs_t when; + bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode, + &flags, &value, &when); + + env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId); + env->SetIntField(event, gInputOffsets.mType, (jint)type); + env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode); + env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode); + env->SetIntField(event, gInputOffsets.mFlags, (jint)flags); + env->SetIntField(event, gInputOffsets.mValue, value); + env->SetLongField(event, gInputOffsets.mWhen, + (jlong)(nanoseconds_to_milliseconds(when))); + + return res; +} + +static jint +android_server_KeyInputQueue_getDeviceClasses(JNIEnv* env, jobject clazz, + jint deviceId) +{ + jint classes = 0; + gLock.lock(); + if (gHub != NULL) classes = gHub->getDeviceClasses(deviceId); + gLock.unlock(); + return classes; +} + +static jstring +android_server_KeyInputQueue_getDeviceName(JNIEnv* env, jobject clazz, + jint deviceId) +{ + String8 name; + gLock.lock(); + if (gHub != NULL) name = gHub->getDeviceName(deviceId); + gLock.unlock(); + + if (name.size() > 0) { + return env->NewStringUTF(name.string()); + } + return NULL; +} + +static jboolean +android_server_KeyInputQueue_getAbsoluteInfo(JNIEnv* env, jobject clazz, + jint deviceId, jint axis, + jobject info) +{ + int32_t minValue, maxValue, flat, fuzz; + int res = -1; + gLock.lock(); + if (gHub != NULL) { + res = gHub->getAbsoluteInfo(deviceId, axis, + &minValue, &maxValue, &flat, &fuzz); + } + gLock.unlock(); + + if (res < 0) return JNI_FALSE; + + env->SetIntField(info, gInputOffsets.mMinValue, (jint)minValue); + env->SetIntField(info, gInputOffsets.mMaxValue, (jint)maxValue); + env->SetIntField(info, gInputOffsets.mFlat, (jint)flat); + env->SetIntField(info, gInputOffsets.mFuzz, (jint)fuzz); + return JNI_TRUE; +} + +static jint +android_server_KeyInputQueue_getSwitchState(JNIEnv* env, jobject clazz, + jint sw) +{ + jint st = -1; + gLock.lock(); + if (gHub != NULL) st = gHub->getSwitchState(sw); + gLock.unlock(); + + return st; +} + +static jint +android_server_KeyInputQueue_getSwitchStateDevice(JNIEnv* env, jobject clazz, + jint deviceId, jint sw) +{ + jint st = -1; + gLock.lock(); + if (gHub != NULL) st = gHub->getSwitchState(deviceId, sw); + gLock.unlock(); + + return st; +} + +static jint +android_server_KeyInputQueue_getScancodeState(JNIEnv* env, jobject clazz, + jint sw) +{ + jint st = -1; + gLock.lock(); + if (gHub != NULL) st = gHub->getScancodeState(sw); + gLock.unlock(); + + return st; +} + +static jint +android_server_KeyInputQueue_getScancodeStateDevice(JNIEnv* env, jobject clazz, + jint deviceId, jint sw) +{ + jint st = -1; + gLock.lock(); + if (gHub != NULL) st = gHub->getScancodeState(deviceId, sw); + gLock.unlock(); + + return st; +} + +static jint +android_server_KeyInputQueue_getKeycodeState(JNIEnv* env, jobject clazz, + jint sw) +{ + jint st = -1; + gLock.lock(); + if (gHub != NULL) st = gHub->getKeycodeState(sw); + gLock.unlock(); + + return st; +} + +static jint +android_server_KeyInputQueue_getKeycodeStateDevice(JNIEnv* env, jobject clazz, + jint deviceId, jint sw) +{ + jint st = -1; + gLock.lock(); + if (gHub != NULL) st = gHub->getKeycodeState(deviceId, sw); + gLock.unlock(); + + return st; +} + + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gInputMethods[] = { + /* name, signature, funcPtr */ + { "readEvent", "(Landroid/view/RawInputEvent;)Z", + (void*) android_server_KeyInputQueue_readEvent }, + { "getDeviceClasses", "(I)I", + (void*) android_server_KeyInputQueue_getDeviceClasses }, + { "getDeviceName", "(I)Ljava/lang/String;", + (void*) android_server_KeyInputQueue_getDeviceName }, + { "getAbsoluteInfo", "(IILcom/android/server/InputDevice$AbsoluteInfo;)Z", + (void*) android_server_KeyInputQueue_getAbsoluteInfo }, + { "getSwitchState", "(I)I", + (void*) android_server_KeyInputQueue_getSwitchState }, + { "getSwitchState", "(II)I", + (void*) android_server_KeyInputQueue_getSwitchStateDevice }, + { "getScancodeState", "(I)I", + (void*) android_server_KeyInputQueue_getScancodeState }, + { "getScancodeState", "(II)I", + (void*) android_server_KeyInputQueue_getScancodeStateDevice }, + { "getKeycodeState", "(I)I", + (void*) android_server_KeyInputQueue_getKeycodeState }, + { "getKeycodeState", "(II)I", + (void*) android_server_KeyInputQueue_getKeycodeStateDevice }, +}; + +int register_android_server_KeyInputQueue(JNIEnv* env) +{ + jclass input = env->FindClass("com/android/server/KeyInputQueue"); + LOG_FATAL_IF(input == NULL, "Unable to find class com/android/server/KeyInputQueue"); + int res = jniRegisterNativeMethods(env, "com/android/server/KeyInputQueue", + gInputMethods, NELEM(gInputMethods)); + + jclass absoluteInfo = env->FindClass("com/android/server/InputDevice$AbsoluteInfo"); + LOG_FATAL_IF(absoluteInfo == NULL, "Unable to find class com/android/server/InputDevice$AbsoluteInfo"); + + gInputOffsets.mMinValue + = env->GetFieldID(absoluteInfo, "minValue", "I"); + LOG_FATAL_IF(gInputOffsets.mMinValue == NULL, "Unable to find InputDevice.AbsoluteInfo.minValue"); + + gInputOffsets.mMaxValue + = env->GetFieldID(absoluteInfo, "maxValue", "I"); + LOG_FATAL_IF(gInputOffsets.mMaxValue == NULL, "Unable to find InputDevice.AbsoluteInfo.maxValue"); + + gInputOffsets.mFlat + = env->GetFieldID(absoluteInfo, "flat", "I"); + LOG_FATAL_IF(gInputOffsets.mFlat == NULL, "Unable to find InputDevice.AbsoluteInfo.flat"); + + gInputOffsets.mFuzz + = env->GetFieldID(absoluteInfo, "fuzz", "I"); + LOG_FATAL_IF(gInputOffsets.mFuzz == NULL, "Unable to find InputDevice.AbsoluteInfo.fuzz"); + + jclass inputEvent = env->FindClass("android/view/RawInputEvent"); + LOG_FATAL_IF(inputEvent == NULL, "Unable to find class android/view/RawInputEvent"); + + gInputOffsets.mDeviceId + = env->GetFieldID(inputEvent, "deviceId", "I"); + LOG_FATAL_IF(gInputOffsets.mDeviceId == NULL, "Unable to find RawInputEvent.deviceId"); + + gInputOffsets.mType + = env->GetFieldID(inputEvent, "type", "I"); + LOG_FATAL_IF(gInputOffsets.mType == NULL, "Unable to find RawInputEvent.type"); + + gInputOffsets.mScancode + = env->GetFieldID(inputEvent, "scancode", "I"); + LOG_FATAL_IF(gInputOffsets.mScancode == NULL, "Unable to find RawInputEvent.scancode"); + + gInputOffsets.mKeycode + = env->GetFieldID(inputEvent, "keycode", "I"); + LOG_FATAL_IF(gInputOffsets.mKeycode == NULL, "Unable to find RawInputEvent.keycode"); + + gInputOffsets.mFlags + = env->GetFieldID(inputEvent, "flags", "I"); + LOG_FATAL_IF(gInputOffsets.mFlags == NULL, "Unable to find RawInputEvent.flags"); + + gInputOffsets.mValue + = env->GetFieldID(inputEvent, "value", "I"); + LOG_FATAL_IF(gInputOffsets.mValue == NULL, "Unable to find RawInputEvent.value"); + + gInputOffsets.mWhen + = env->GetFieldID(inputEvent, "when", "J"); + LOG_FATAL_IF(gInputOffsets.mWhen == NULL, "Unable to find RawInputEvent.when"); + + return res; +} + +}; // namespace android + diff --git a/core/jni/server/com_android_server_SensorService.cpp b/core/jni/server/com_android_server_SensorService.cpp new file mode 100644 index 0000000..37f6231 --- /dev/null +++ b/core/jni/server/com_android_server_SensorService.cpp @@ -0,0 +1,107 @@ +/* + * 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 "Sensors" + +#include <hardware/sensors.h> + +#include "jni.h" +#include "JNIHelp.h" + +namespace android { + + +static struct file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; + jfieldID mDescriptor; +} gFileDescriptorOffsets; + +static struct parcel_file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; +} gParcelFileDescriptorOffsets; + +/* + * The method below are not thread-safe and not intended to be + */ + +static jint +android_init(JNIEnv *env, jclass clazz) +{ + return sensors_control_init(); +} + +static jobject +android_open(JNIEnv *env, jclass clazz) +{ + int fd = sensors_control_open(); + // new FileDescriptor() + jobject filedescriptor = env->NewObject( + gFileDescriptorOffsets.mClass, + gFileDescriptorOffsets.mConstructor); + + if (filedescriptor != NULL) { + env->SetIntField(filedescriptor, gFileDescriptorOffsets.mDescriptor, fd); + // new ParcelFileDescriptor() + return env->NewObject(gParcelFileDescriptorOffsets.mClass, + gParcelFileDescriptorOffsets.mConstructor, + filedescriptor); + } + close(fd); + return NULL; +} + +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; +} + +static jint +android_set_delay(JNIEnv *env, jclass clazz, jint ms) +{ + return sensors_control_delay(ms); +} + +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_set_delay","(I)I", (void*) android_set_delay }, +}; + +int register_android_server_SensorService(JNIEnv *env) +{ + jclass clazz; + + clazz = env->FindClass("java/io/FileDescriptor"); + gFileDescriptorOffsets.mClass = (jclass)env->NewGlobalRef(clazz); + gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V"); + gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I"); + + clazz = env->FindClass("android/os/ParcelFileDescriptor"); + gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); + + return jniRegisterNativeMethods(env, "com/android/server/SensorService", + gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/core/jni/server/com_android_server_SystemServer.cpp b/core/jni/server/com_android_server_SystemServer.cpp new file mode 100644 index 0000000..ae29405 --- /dev/null +++ b/core/jni/server/com_android_server_SystemServer.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <utils/Log.h> +#include <utils/misc.h> + +#include "jni.h" +#include "JNIHelp.h" + +namespace android { + +extern "C" int system_init(); + +static void android_server_SystemServer_init1(JNIEnv* env, jobject clazz) +{ + system_init(); +} + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "init1", "([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 }, +}; + +int register_android_server_SystemServer(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "com/android/server/SystemServer", + gMethods, NELEM(gMethods)); +} + +}; // namespace android + + diff --git a/core/jni/server/onload.cpp b/core/jni/server/onload.cpp new file mode 100644 index 0000000..3d68cfb --- /dev/null +++ b/core/jni/server/onload.cpp @@ -0,0 +1,36 @@ +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +namespace android { +int register_android_server_AlarmManagerService(JNIEnv* env); +int register_android_server_BatteryService(JNIEnv* env); +int register_android_server_KeyInputQueue(JNIEnv* env); +int register_android_os_Vibrator(JNIEnv* env); +int register_android_server_SensorService(JNIEnv* env); +int register_android_server_SystemServer(JNIEnv* env); +}; + +using namespace android; + +extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + JNIEnv* env = NULL; + jint result = -1; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + LOGE("GetEnv failed!"); + return result; + } + LOG_ASSERT(env, "Could not retrieve the env!"); + + register_android_server_KeyInputQueue(env); + register_android_os_Vibrator(env); + register_android_server_AlarmManagerService(env); + register_android_server_BatteryService(env); + register_android_server_SensorService(env); + register_android_server_SystemServer(env); + + return JNI_VERSION_1_4; +} diff --git a/core/jni/sqlite3_exception.h b/core/jni/sqlite3_exception.h new file mode 100644 index 0000000..13735a1 --- /dev/null +++ b/core/jni/sqlite3_exception.h @@ -0,0 +1,47 @@ +/* //device/libs/include/android_runtime/sqlite3_exception.h +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** 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. +*/ + +#ifndef _SQLITE3_EXCEPTION_H +#define _SQLITE3_EXCEPTION_H 1 + +#include <jni.h> +#include <JNIHelp.h> +//#include <android_runtime/AndroidRuntime.h> + +#include <sqlite3.h> + +namespace android { + +/* throw a SQLiteException with a message appropriate for the error in handle */ +void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle); + +/* throw a SQLiteException with the given message */ +void throw_sqlite3_exception(JNIEnv* env, const char* message); + +/* throw a SQLiteException with a message appropriate for the error in handle + concatenated with the given message + */ +void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message); + +/* throw a SQLiteException for a given error code */ +void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message); + +void throw_sqlite3_exception(JNIEnv* env, int errcode, + const char* sqlite3Message, const char* message); +} + +#endif // _SQLITE3_EXCEPTION_H |