diff options
Diffstat (limited to 'core/jni')
29 files changed, 2268 insertions, 1637 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index a038cc5..8310e56 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -19,12 +19,15 @@ ifneq ($(USE_CUSTOM_RUNTIME_HEAP_MAX),) LOCAL_CFLAGS += -DCUSTOM_RUNTIME_HEAP_MAX=$(USE_CUSTOM_RUNTIME_HEAP_MAX) endif +ifeq ($(USE_OPENGL_RENDERER),true) + LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER +endif + LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES LOCAL_SRC_FILES:= \ ActivityManager.cpp \ AndroidRuntime.cpp \ - CursorWindow.cpp \ Time.cpp \ com_google_android_gles_jni_EGLImpl.cpp \ com_google_android_gles_jni_GLImpl.cpp.arm \ @@ -48,6 +51,7 @@ LOCAL_SRC_FILES:= \ android_view_InputChannel.cpp \ android_view_InputQueue.cpp \ android_view_KeyEvent.cpp \ + android_view_GLES20Canvas.cpp \ android_view_MotionEvent.cpp \ android_text_AndroidCharacter.cpp \ android_text_AndroidBidi.cpp \ @@ -105,6 +109,7 @@ LOCAL_SRC_FILES:= \ android/graphics/Rasterizer.cpp \ android/graphics/Region.cpp \ android/graphics/Shader.cpp \ + android/graphics/TextLayout.cpp \ android/graphics/Typeface.cpp \ android/graphics/Xfermode.cpp \ android/graphics/YuvToJpegEncoder.cpp \ @@ -123,7 +128,6 @@ LOCAL_SRC_FILES:= \ android_bluetooth_common.cpp \ android_bluetooth_BluetoothAudioGateway.cpp \ android_bluetooth_BluetoothSocket.cpp \ - android_bluetooth_ScoSocket.cpp \ android_server_BluetoothService.cpp \ android_server_BluetoothEventLoop.cpp \ android_server_BluetoothA2dpService.cpp \ @@ -142,6 +146,7 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ $(LOCAL_PATH)/android/graphics \ + $(LOCAL_PATH)/../../libs/hwui \ $(call include-path-for, bluedroid) \ $(call include-path-for, libhardware)/hardware \ $(call include-path-for, libhardware_legacy)/hardware_legacy \ @@ -192,6 +197,10 @@ LOCAL_SHARED_LIBRARIES := \ libwpa_client \ libjpeg +ifeq ($(USE_OPENGL_RENDERER),true) + LOCAL_SHARED_LIBRARIES += libhwui +endif + ifeq ($(BOARD_HAVE_BLUETOOTH),true) LOCAL_C_INCLUDES += \ external/dbus \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 2751a82..c9d7e7f 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -115,6 +115,7 @@ 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_GLES20Canvas(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); @@ -151,7 +152,6 @@ extern int register_android_opengl_classes(JNIEnv *env); extern int register_android_bluetooth_HeadsetBase(JNIEnv* env); extern int register_android_bluetooth_BluetoothAudioGateway(JNIEnv* env); extern int register_android_bluetooth_BluetoothSocket(JNIEnv *env); -extern int register_android_bluetooth_ScoSocket(JNIEnv *env); extern int register_android_server_BluetoothService(JNIEnv* env); extern int register_android_server_BluetoothEventLoop(JNIEnv *env); extern int register_android_server_BluetoothA2dpService(JNIEnv* env); @@ -1232,6 +1232,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_nio_utils), REG_JNI(register_android_graphics_PixelFormat), REG_JNI(register_android_graphics_Graphics), + REG_JNI(register_android_view_GLES20Canvas), REG_JNI(register_android_view_Surface), REG_JNI(register_android_view_ViewRoot), REG_JNI(register_com_google_android_gles_jni_EGLImpl), @@ -1303,7 +1304,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_bluetooth_HeadsetBase), REG_JNI(register_android_bluetooth_BluetoothAudioGateway), REG_JNI(register_android_bluetooth_BluetoothSocket), - REG_JNI(register_android_bluetooth_ScoSocket), REG_JNI(register_android_server_BluetoothService), REG_JNI(register_android_server_BluetoothEventLoop), REG_JNI(register_android_server_BluetoothA2dpService), diff --git a/core/jni/CursorWindow.cpp b/core/jni/CursorWindow.cpp deleted file mode 100644 index 7877921..0000000 --- a/core/jni/CursorWindow.cpp +++ /dev/null @@ -1,413 +0,0 @@ -/* - * 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 <binder/MemoryHeapBase.h> -#include <binder/MemoryBase.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(const 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 - 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 - - sp<MemoryHeapBase> heap; - heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow"); - if (heap != NULL) { - mMemory = new MemoryBase(heap, 0, 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("CursorWindow heap allocation failed"); - return false; - } else { - LOGE("failed to create the CursorWindow heap"); - 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 deleted file mode 100644 index 3fcb560..0000000 --- a/core/jni/CursorWindow.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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 <binder/IMemory.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(const 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<IMemory> mMemory; - - /** - * Offset of the lowest unused data byte in the array. - */ - uint32_t mFreeOffset; -}; - -}; // namespace android - -#endif diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 88bbafd..b062264 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -313,6 +313,10 @@ static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) { return bitmap->config();
}
+static int Bitmap_getGenerationId(JNIEnv* env, jobject, SkBitmap* bitmap) {
+ return bitmap->getGenerationID();
+}
+
static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) {
return !bitmap->isOpaque();
}
@@ -606,6 +610,7 @@ static JNINativeMethod gBitmapMethods[] = { (void*)Bitmap_writeToParcel },
{ "nativeExtractAlpha", "(II[I)Landroid/graphics/Bitmap;",
(void*)Bitmap_extractAlpha },
+ { "nativeGenerationId", "(I)I", (void*)Bitmap_getGenerationId },
{ "nativeGetPixel", "(III)I", (void*)Bitmap_getPixel },
{ "nativeGetPixels", "(I[IIIIIII)V", (void*)Bitmap_getPixels },
{ "nativeSetPixel", "(IIII)V", (void*)Bitmap_setPixel },
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index e1e9536..bf150a9 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -20,7 +20,6 @@ #include "SkCanvas.h" #include "SkDevice.h" -#include "SkGLCanvas.h" #include "SkGraphics.h" #include "SkImageRef_GlobalPool.h" #include "SkPorterDuff.h" @@ -30,6 +29,13 @@ #include "SkBoundaryPatch.h" #include "SkMeshUtils.h" +#include "TextLayout.h" + +#include "unicode/ubidi.h" +#include "unicode/ushape.h" + +#include <utils/Log.h> + #define TIME_DRAWx static uint32_t get_thread_msec() { @@ -60,13 +66,8 @@ public: return bitmap ? new SkCanvas(*bitmap) : new SkCanvas; } - static SkCanvas* initGL(JNIEnv* env, jobject) { - return new SkGLCanvas; - } - static void freeCaches(JNIEnv* env, jobject) { // these are called in no particular order - SkGLCanvas::DeleteAllTextures(); SkImageRef_GlobalPool::SetRAMUsed(0); SkGraphics::SetFontCacheUsed(0); } @@ -103,11 +104,6 @@ public: 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); @@ -743,45 +739,49 @@ public: canvas->drawVertices(mode, ptCount, verts, texs, colors, NULL, indices, indexCount, *paint); } - - static void drawText___CIIFFPaint(JNIEnv* env, jobject, SkCanvas* canvas, + + + static void drawText___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index, int count, - jfloat x, jfloat y, SkPaint* paint) { + jfloat x, jfloat y, int flags, SkPaint* paint) { jchar* textArray = env->GetCharArrayElements(text, NULL); - jsize textCount = env->GetArrayLength(text); - SkScalar x_ = SkFloatToScalar(x); - SkScalar y_ = SkFloatToScalar(y); - canvas->drawText(textArray + index, count << 1, x_, y_, *paint); - env->ReleaseCharArrayElements(text, textArray, 0); + TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas); + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } - - 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 drawText__StringIIFFIPaint(JNIEnv* env, jobject, + SkCanvas* canvas, jstring text, + int start, int end, + jfloat x, jfloat y, int flags, SkPaint* paint) { + const jchar* textArray = env->GetStringChars(text, NULL); + TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas); + env->ReleaseStringChars(text, textArray); } - - 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 drawTextRun___CIIIIFFIPaint( + JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index, + int count, int contextIndex, int contextCount, + jfloat x, jfloat y, int dirFlags, SkPaint* paint) { + + jchar* chars = env->GetCharArrayElements(text, NULL); + TextLayout::drawTextRun(paint, chars + contextIndex, index - contextIndex, + count, contextCount, dirFlags, x, y, canvas); + env->ReleaseCharArrayElements(text, chars, JNI_ABORT); } - + + static void drawTextRun__StringIIIIFFIPaint( + JNIEnv* env, jobject obj, SkCanvas* canvas, jstring text, jint start, + jint end, jint contextStart, jint contextEnd, + jfloat x, jfloat y, jint dirFlags, SkPaint* paint) { + + jint count = end - start; + jint contextCount = contextEnd - contextStart; + const jchar* chars = env->GetStringChars(text, NULL); + TextLayout::drawTextRun(paint, chars + contextStart, start - contextStart, + count, contextCount, dirFlags, x, y, canvas); + env->ReleaseStringChars(text, chars); + } + static void drawPosText___CII_FPaint(JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index, int count, jfloatArray pos, SkPaint* paint) { @@ -804,10 +804,11 @@ public: } delete[] posPtr; } - + static void drawPosText__String_FPaint(JNIEnv* env, jobject, SkCanvas* canvas, jstring text, - jfloatArray pos, SkPaint* paint) { + 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; @@ -827,27 +828,27 @@ public: } 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) { + SkCanvas* canvas, jcharArray text, int index, int count, + SkPath* path, jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) { jchar* textArray = env->GetCharArrayElements(text, NULL); - canvas->drawTextOnPathHV(textArray + index, count << 1, *path, - SkFloatToScalar(hOffset), SkFloatToScalar(vOffset), *paint); + TextLayout::drawTextOnPath(paint, textArray, count, bidiFlags, hOffset, vOffset, + path, canvas); env->ReleaseCharArrayElements(text, textArray, 0); } - + static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject, - SkCanvas* canvas, jstring text, SkPath* path, - jfloat hOffset, jfloat vOffset, SkPaint* paint) { + SkCanvas* canvas, jstring text, SkPath* path, + jfloat hOffset, jfloat vOffset, jint bidiFlags, 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); + int count = env->GetStringLength(text); + TextLayout::drawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset, + path, canvas); env->ReleaseStringChars(text, text_); } - + static bool getClipBounds(JNIEnv* env, jobject, SkCanvas* canvas, jobject bounds) { SkRect r; @@ -868,12 +869,10 @@ public: 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", @@ -940,26 +939,27 @@ static JNINativeMethod gCanvasMethods[] = { (void*) SkCanvasGlue::drawBitmapRR}, {"native_drawBitmap", "(I[IIIFFIIZI)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_drawText","(I[CIIFFII)V", + (void*) SkCanvasGlue::drawText___CIIFFIPaint}, + {"native_drawText","(ILjava/lang/String;IIFFII)V", + (void*) SkCanvasGlue::drawText__StringIIFFIPaint}, + {"native_drawTextRun","(I[CIIIIFFII)V", + (void*) SkCanvasGlue::drawTextRun___CIIIIFFIPaint}, + {"native_drawTextRun","(ILjava/lang/String;IIIIFFII)V", + (void*) SkCanvasGlue::drawTextRun__StringIIIIFFIPaint}, {"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", + {"native_drawTextOnPath","(I[CIIIFFII)V", (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint}, - {"native_drawTextOnPath","(ILjava/lang/String;IFFI)V", + {"native_drawTextOnPath","(ILjava/lang/String;IFFII)V", (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint}, {"native_drawPicture", "(II)V", (void*) SkCanvasGlue::drawPicture}, diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp index ebfb209..f3be8b0 100644 --- a/core/jni/android/graphics/ColorFilter.cpp +++ b/core/jni/android/graphics/ColorFilter.cpp @@ -23,28 +23,69 @@ #include "SkColorMatrixFilter.h" #include "SkPorterDuff.h" +#include <SkiaColorFilter.h> + namespace android { +using namespace uirenderer; + class SkColorFilterGlue { public: - - static void finalizer(JNIEnv* env, jobject clazz, SkColorFilter* obj) { + static void finalizer(JNIEnv* env, jobject clazz, SkColorFilter* obj, SkiaColorFilter* f) { + delete f; obj->safeUnref(); } - static SkColorFilter* CreatePorterDuffFilter(JNIEnv* env, jobject, - jint srcColor, SkPorterDuff::Mode mode) { - return SkColorFilter::CreateModeFilter(srcColor, - SkPorterDuff::ToXfermodeMode(mode)); + static SkiaColorFilter* glCreatePorterDuffFilter(JNIEnv* env, jobject, jint srcColor, + SkPorterDuff::Mode mode) { +#ifdef USE_OPENGL_RENDERER + return new SkiaBlendFilter(srcColor, SkPorterDuff::ToXfermodeMode(mode)); +#else + return NULL; +#endif + } + + static SkiaColorFilter* glCreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) { +#ifdef USE_OPENGL_RENDERER + return new SkiaLightingFilter(mul, add); +#else + return NULL; +#endif + } + + static SkiaColorFilter* glCreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) { +#ifdef USE_OPENGL_RENDERER + AutoJavaFloatArray autoArray(env, jarray, 20); + const float* src = autoArray.ptr(); + + float* colorMatrix = new float[16]; + memcpy(colorMatrix, src, 4 * sizeof(float)); + memcpy(&colorMatrix[4], &src[5], 4 * sizeof(float)); + memcpy(&colorMatrix[8], &src[10], 4 * sizeof(float)); + memcpy(&colorMatrix[12], &src[15], 4 * sizeof(float)); + + float* colorVector = new float[4]; + colorVector[0] = src[4]; + colorVector[1] = src[9]; + colorVector[2] = src[14]; + colorVector[3] = src[19]; + + return new SkiaColorMatrixFilter(colorMatrix, colorVector); +#else + return NULL; +#endif + } + + static SkColorFilter* CreatePorterDuffFilter(JNIEnv* env, jobject, jint srcColor, + SkPorterDuff::Mode mode) { + return SkColorFilter::CreateModeFilter(srcColor, SkPorterDuff::ToXfermodeMode(mode)); } - - static SkColorFilter* CreateLightingFilter(JNIEnv* env, jobject, - jint mul, jint add) { + + static SkColorFilter* CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) { return SkColorFilter::CreateLightingFilter(mul, add); } - - static SkColorFilter* CreateColorMatrixFilter(JNIEnv* env, jobject, - jfloatArray jarray) { + + static SkColorFilter* CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) { AutoJavaFloatArray autoArray(env, jarray, 20); const float* src = autoArray.ptr(); @@ -58,26 +99,25 @@ public: return new SkColorMatrixFilter(src); #endif } - }; static JNINativeMethod colorfilter_methods[] = { - {"finalizer", "(I)V", (void*) SkColorFilterGlue::finalizer} + {"finalizer", "(II)V", (void*) SkColorFilterGlue::finalizer} }; static JNINativeMethod porterduff_methods[] = { - {"native_CreatePorterDuffFilter","(II)I", - (void*) SkColorFilterGlue::CreatePorterDuffFilter} + { "native_CreatePorterDuffFilter", "(II)I", (void*) SkColorFilterGlue::CreatePorterDuffFilter }, + { "nCreatePorterDuffFilter", "(II)I", (void*) SkColorFilterGlue::glCreatePorterDuffFilter } }; static JNINativeMethod lighting_methods[] = { - {"native_CreateLightingFilter","(II)I", - (void*) SkColorFilterGlue::CreateLightingFilter} + { "native_CreateLightingFilter", "(II)I", (void*) SkColorFilterGlue::CreateLightingFilter }, + { "nCreateLightingFilter", "(II)I", (void*) SkColorFilterGlue::glCreateLightingFilter }, }; static JNINativeMethod colormatrix_methods[] = { - {"nativeColorMatrixFilter","([F)I", - (void*) SkColorFilterGlue::CreateColorMatrixFilter} + { "nativeColorMatrixFilter", "([F)I", (void*) SkColorFilterGlue::CreateColorMatrixFilter }, + { "nColorMatrixFilter", "([F)I", (void*) SkColorFilterGlue::glCreateColorMatrixFilter } }; #define REG(env, name, array) \ @@ -85,7 +125,6 @@ static JNINativeMethod colormatrix_methods[] = { SK_ARRAY_COUNT(array)); \ if (result < 0) return result - int register_android_graphics_ColorFilter(JNIEnv* env) { int result; diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 780badc..ca9f371 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -31,6 +31,11 @@ #include "SkShader.h" #include "SkTypeface.h" #include "SkXfermode.h" +#include "unicode/ushape.h" +#include "TextLayout.h" + +// temporary for debugging +#include <utils/Log.h> namespace android { @@ -57,6 +62,9 @@ static void defaultSettingsForAndroid(SkPaint* paint) { class SkPaintGlue { public: + enum MoveOpt { + AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT + }; static void finalizer(JNIEnv* env, jobject clazz, SkPaint* obj) { delete obj; @@ -395,20 +403,161 @@ public: 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) { + + static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, const jchar *text, + jint start, jint count, jint contextCount, jint flags, + jfloatArray advances, jint advancesIndex) { + jfloat advancesArray[count]; + jfloat totalAdvance; + + TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, flags, + advancesArray, totalAdvance); + + if (advances != NULL) { + env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray); + } + return totalAdvance; + } + + static float getTextRunAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint, + jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, + jint flags, jfloatArray advances, jint advancesIndex) { + jchar* textArray = env->GetCharArrayElements(text, NULL); + jfloat result = doTextRunAdvances(env, paint, textArray + contextIndex, + index - contextIndex, count, contextCount, flags, advances, advancesIndex); + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); + return result; + } + + static float getTextRunAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint, + jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags, + jfloatArray advances, jint advancesIndex) { + const jchar* textArray = env->GetStringChars(text, NULL); + jfloat result = doTextRunAdvances(env, paint, textArray + contextStart, + start - contextStart, end - start, contextEnd - contextStart, flags, advances, + advancesIndex); + env->ReleaseStringChars(text, textArray); + return result; + } + + static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start, + jint count, jint flags, jint offset, jint opt) { + SkScalar scalarArray[count]; + jchar buffer[count]; + + // this is where we'd call harfbuzz + // for now we just use ushape.c and widths returned from skia + + int widths; + if (flags & 0x1) { // rtl, call arabic shaping in case + UErrorCode status = U_ZERO_ERROR; + // Use fixed length since we need to keep start and count valid + u_shapeArabic(text + start, count, buffer, count, + U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_LOGICAL | + U_SHAPE_LETTERS_SHAPE | U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); + // we shouldn't fail unless there's an out of memory condition, + // in which case we're hosed anyway + for (int i = 0; i < count; ++i) { + if (buffer[i] == 0xffff) { + buffer[i] = 0x200b; // zero-width-space for skia + } + } + widths = paint->getTextWidths(buffer, count << 1, scalarArray); + } else { + widths = paint->getTextWidths(text + start, count << 1, scalarArray); + } + + if (widths < count) { + // Skia operates on code points, not code units, so surrogate pairs return only one + // value. Expand the result so we have one value per UTF-16 code unit. + + // Note, skia's getTextWidth gets confused if it encounters a surrogate pair, + // leaving the remaining widths zero. Not nice. + const jchar *chars = text + start; + for (int i = count, p = widths - 1; --i > p;) { + if (chars[i] >= 0xdc00 && chars[i] < 0xe000 && + chars[i-1] >= 0xd800 && chars[i-1] < 0xdc00) { + scalarArray[i] = 0; + } else { + scalarArray[i] = scalarArray[--p]; + } + } + } + + jint pos = offset - start; + switch (opt) { + case AFTER: + if (pos < count) { + pos += 1; + } + // fall through + case AT_OR_AFTER: + while (pos < count && scalarArray[pos] == 0) { + ++pos; + } + break; + case BEFORE: + if (pos > 0) { + --pos; + } + // fall through + case AT_OR_BEFORE: + while (pos > 0 && scalarArray[pos] == 0) { + --pos; + } + break; + case AT: + default: + if (scalarArray[pos] == 0) { + pos = -1; + } + break; + } + + if (pos != -1) { + pos += start; + } + + return pos; + } + + static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, + jint contextStart, jint contextCount, jint flags, jint offset, jint cursorOpt) { + jchar* textArray = env->GetCharArrayElements(text, NULL); + jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, flags, + offset, cursorOpt); + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); + return result; + } + + static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, + jint contextStart, jint contextEnd, jint flags, jint offset, jint cursorOpt) { + const jchar* textArray = env->GetStringChars(text, NULL); + jint result = doTextRunCursor(env, paint, textArray, contextStart, + contextEnd - contextStart, flags, offset, cursorOpt); + env->ReleaseStringChars(text, textArray); + return result; + } + + static void getTextPath(JNIEnv* env, SkPaint* paint, const jchar* text, jint count, + jint bidiFlags, jfloat x, jfloat y, SkPath *path) { + TextLayout::getTextPath(paint, text, count, bidiFlags, x, y, path); + } + + static void getTextPath___C(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags, + jcharArray text, int index, int count, jfloat x, jfloat y, SkPath* path) { const jchar* textArray = env->GetCharArrayElements(text, NULL); - paint->getTextPath(textArray + index, count << 1, SkFloatToScalar(x), SkFloatToScalar(y), path); - env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), - JNI_ABORT); + getTextPath(env, paint, textArray + index, count, bidiFlags, x, y, path); + env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); } - - static void getTextPath__StringIIFFPath(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, int start, int end, jfloat x, jfloat y, SkPath* path) { + + static void getTextPath__String(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags, + 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); + getTextPath(env, paint, textArray + start, end - start, bidiFlags, x, 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); @@ -576,13 +725,20 @@ static JNINativeMethod methods[] = { {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS}, {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F}, {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F}, - {"native_getTextPath","(I[CIIFFI)V", (void*) SkPaintGlue::getTextPath___CIIFFPath}, - {"native_getTextPath","(ILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__StringIIFFPath}, + {"native_getTextRunAdvances","(I[CIIIII[FI)F", (void*) + SkPaintGlue::getTextRunAdvances___CIIIII_FI}, + {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F", + (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI}, + {"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C}, + {"native_getTextRunCursor", "(ILjava/lang/String;IIIII)I", + (void*) SkPaintGlue::getTextRunCursor__String}, + {"native_getTextPath","(II[CIIFFI)V", (void*) SkPaintGlue::getTextPath___C}, + {"native_getTextPath","(IILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__String}, {"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} + {"nSetShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer} }; static jfieldID req_fieldID(jfieldID id) { diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index b09c62b..cb1c333 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -8,6 +8,15 @@ #include "SkTemplates.h" #include "SkXfermode.h" +#include <SkiaShader.h> + +using namespace android::uirenderer; + +static struct { + jclass clazz; + jfieldID shader; +} gShaderClassInfo; + static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) { if (NULL == ptr) { doThrowIAE(env); @@ -41,8 +50,9 @@ static int Color_HSVToColor(JNIEnv* env, jobject, int alpha, jfloatArray hsvArra /////////////////////////////////////////////////////////////////////////////////////////////// -static void Shader_destructor(JNIEnv* env, jobject, SkShader* shader) +static void Shader_destructor(JNIEnv* env, jobject o, SkShader* shader, SkiaShader* skiaShader) { + delete skiaShader; shader->safeUnref(); } @@ -51,7 +61,8 @@ static bool Shader_getLocalMatrix(JNIEnv* env, jobject, const SkShader* shader, return shader ? shader->getLocalMatrix(matrix) : false; } -static void Shader_setLocalMatrix(JNIEnv* env, jobject, SkShader* shader, const SkMatrix* matrix) +static void Shader_setLocalMatrix(JNIEnv* env, jobject o, SkShader* shader, SkiaShader* skiaShader, + const SkMatrix* matrix) { if (shader) { if (NULL == matrix) { @@ -60,24 +71,40 @@ static void Shader_setLocalMatrix(JNIEnv* env, jobject, SkShader* shader, const else { shader->setLocalMatrix(*matrix); } +#ifdef USE_OPENGL_RENDERER + skiaShader->setMatrix(const_cast<SkMatrix*>(matrix)); +#endif } } /////////////////////////////////////////////////////////////////////////////////////////////// -static SkShader* BitmapShader_constructor(JNIEnv* env, jobject, const SkBitmap* bitmap, +static SkShader* BitmapShader_constructor(JNIEnv* env, jobject o, const SkBitmap* bitmap, int tileModeX, int tileModeY) { SkShader* s = SkShader::CreateBitmapShader(*bitmap, (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY); + ThrowIAE_IfNull(env, s); return s; } + +static SkiaShader* BitmapShader_postConstructor(JNIEnv* env, jobject o, SkShader* shader, + SkBitmap* bitmap, int tileModeX, int tileModeY) { +#ifdef USE_OPENGL_RENDERER + SkiaShader* skiaShader = new SkiaBitmapShader(bitmap, shader, + static_cast<SkShader::TileMode>(tileModeX), static_cast<SkShader::TileMode>(tileModeY), + NULL, (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0); + return skiaShader; +#else + return NULL; +#endif +} /////////////////////////////////////////////////////////////////////////////////////////////// -static SkShader* LinearGradient_create1(JNIEnv* env, jobject, +static SkShader* LinearGradient_create1(JNIEnv* env, jobject o, float x0, float y0, float x1, float y1, jintArray colorArray, jfloatArray posArray, int tileMode) { @@ -85,31 +112,95 @@ static SkShader* LinearGradient_create1(JNIEnv* env, jobject, pts[0].set(SkFloatToScalar(x0), SkFloatToScalar(y0)); pts[1].set(SkFloatToScalar(x1), SkFloatToScalar(y1)); - size_t count = env->GetArrayLength(colorArray); + size_t count = env->GetArrayLength(colorArray); const jint* 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++) + for (size_t i = 0; i < count; i++) { pos[i] = SkFloatToScalar(posValues[i]); + } } - + SkShader* shader = SkGradientShader::CreateLinear(pts, reinterpret_cast<const SkColor*>(colorValues), pos, count, static_cast<SkShader::TileMode>(tileMode)); - env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), - JNI_ABORT); + + env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT); ThrowIAE_IfNull(env, shader); return shader; } -static SkShader* LinearGradient_create2(JNIEnv* env, jobject, +static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader* shader, + float x0, float y0, float x1, float y1, jintArray colorArray, + jfloatArray posArray, int tileMode) { +#ifdef USE_OPENGL_RENDERER + size_t count = env->GetArrayLength(colorArray); + const jint* colorValues = env->GetIntArrayElements(colorArray, NULL); + + jfloat* storedBounds = new jfloat[4]; + storedBounds[0] = x0; storedBounds[1] = y0; + storedBounds[2] = x1; storedBounds[3] = y1; + jfloat* storedPositions = new jfloat[count]; + uint32_t* storedColors = new uint32_t[count]; + for (size_t i = 0; i < count; i++) { + storedColors[i] = static_cast<uint32_t>(colorValues[i]); + } + + if (posArray) { + AutoJavaFloatArray autoPos(env, posArray, count); + const float* posValues = autoPos.ptr(); + for (size_t i = 0; i < count; i++) { + storedPositions[i] = posValues[i]; + } + } else { + storedPositions[0] = 0.0f; + storedPositions[1] = 1.0f; + } + + SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors, + storedPositions, count, shader, static_cast<SkShader::TileMode>(tileMode), NULL, + (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0); + + env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT); + return skiaShader; +#else + return NULL; +#endif +} + +static SkiaShader* LinearGradient_postCreate2(JNIEnv* env, jobject o, SkShader* shader, + float x0, float y0, float x1, float y1, int color0, int color1, int tileMode) { +#ifdef USE_OPENGL_RENDERER + float* storedBounds = new float[4]; + storedBounds[0] = x0; storedBounds[1] = y0; + storedBounds[2] = x1; storedBounds[3] = y1; + + float* storedPositions = new float[2]; + storedPositions[0] = 0.0f; + storedPositions[1] = 1.0f; + + uint32_t* storedColors = new uint32_t[2]; + storedColors[0] = static_cast<uint32_t>(color0); + storedColors[1] = static_cast<uint32_t>(color1); + + SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors, + storedPositions, 2, shader, static_cast<SkShader::TileMode>(tileMode), NULL, + (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0); + + return skiaShader; +#else + return NULL; +#endif +} + +static SkShader* LinearGradient_create2(JNIEnv* env, jobject o, float x0, float y0, float x1, float y1, int color0, int color1, int tileMode) { @@ -120,8 +211,9 @@ static SkShader* LinearGradient_create2(JNIEnv* env, jobject, SkColor colors[2]; colors[0] = color0; colors[1] = color1; - + SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode); + ThrowIAE_IfNull(env, s); return s; } @@ -222,18 +314,42 @@ static SkShader* SweepGradient_create2(JNIEnv* env, jobject, float x, float y, /////////////////////////////////////////////////////////////////////////////////////////////// -static SkShader* ComposeShader_create1(JNIEnv* env, jobject, - SkShader* shaderA, SkShader* shaderB, SkXfermode* mode) +static SkShader* ComposeShader_create1(JNIEnv* env, jobject o, + 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) +static SkShader* ComposeShader_create2(JNIEnv* env, jobject o, + SkShader* shaderA, SkShader* shaderB, SkPorterDuff::Mode porterDuffMode) { - SkAutoUnref au(SkPorterDuff::CreateXfermode(mode)); + SkAutoUnref au(SkPorterDuff::CreateXfermode(porterDuffMode)); + SkXfermode* mode = (SkXfermode*) au.get(); + return new SkComposeShader(shaderA, shaderB, mode); +} - return new SkComposeShader(shaderA, shaderB, (SkXfermode*)au.get()); +static SkiaShader* ComposeShader_postCreate2(JNIEnv* env, jobject o, SkShader* shader, + SkiaShader* shaderA, SkiaShader* shaderB, SkPorterDuff::Mode porterDuffMode) { +#ifdef USE_OPENGL_RENDERER + SkXfermode::Mode mode = SkPorterDuff::ToXfermodeMode(porterDuffMode); + return new SkiaComposeShader(shaderA, shaderB, mode, shader); +#else + return NULL; +#endif +} + +static SkiaShader* ComposeShader_postCreate1(JNIEnv* env, jobject o, SkShader* shader, + SkiaShader* shaderA, SkiaShader* shaderB, SkXfermode* mode) { +#ifdef USE_OPENGL_RENDERER + SkXfermode::Mode skiaMode; + if (!SkXfermode::IsMode(mode, &skiaMode)) { + // TODO: Support other modes + skiaMode = SkXfermode::kSrcOver_Mode; + } + return new SkiaComposeShader(shaderA, shaderB, skiaMode, shader); +#else + return NULL; +#endif } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -244,18 +360,21 @@ static JNINativeMethod gColorMethods[] = { }; static JNINativeMethod gShaderMethods[] = { - { "nativeDestructor", "(I)V", (void*)Shader_destructor }, + { "nativeDestructor", "(II)V", (void*)Shader_destructor }, { "nativeGetLocalMatrix", "(II)Z", (void*)Shader_getLocalMatrix }, - { "nativeSetLocalMatrix", "(II)V", (void*)Shader_setLocalMatrix } + { "nativeSetLocalMatrix", "(III)V", (void*)Shader_setLocalMatrix } }; static JNINativeMethod gBitmapShaderMethods[] = { - { "nativeCreate", "(III)I", (void*)BitmapShader_constructor } + { "nativeCreate", "(III)I", (void*)BitmapShader_constructor }, + { "nativePostCreate", "(IIII)I", (void*)BitmapShader_postConstructor } }; static JNINativeMethod gLinearGradientMethods[] = { - { "nativeCreate1", "(FFFF[I[FI)I", (void*)LinearGradient_create1 }, - { "nativeCreate2", "(FFFFIII)I", (void*)LinearGradient_create2 } + { "nativeCreate1", "(FFFF[I[FI)I", (void*)LinearGradient_create1 }, + { "nativeCreate2", "(FFFFIII)I", (void*)LinearGradient_create2 }, + { "nativePostCreate1", "(IFFFF[I[FI)I", (void*)LinearGradient_postCreate1 }, + { "nativePostCreate2", "(IFFFFIII)I", (void*)LinearGradient_postCreate2 } }; static JNINativeMethod gRadialGradientMethods[] = { @@ -269,8 +388,10 @@ static JNINativeMethod gSweepGradientMethods[] = { }; static JNINativeMethod gComposeShaderMethods[] = { - {"nativeCreate1", "(III)I", (void*)ComposeShader_create1 }, - {"nativeCreate2", "(III)I", (void*)ComposeShader_create2 } + {"nativeCreate1", "(III)I", (void*)ComposeShader_create1 }, + {"nativeCreate2", "(III)I", (void*)ComposeShader_create2 }, + {"nativePostCreate1", "(IIII)I", (void*)ComposeShader_postCreate1 }, + {"nativePostCreate2", "(IIII)I", (void*)ComposeShader_postCreate2 } }; #include <android_runtime/AndroidRuntime.h> diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp new file mode 100644 index 0000000..147e1fa --- /dev/null +++ b/core/jni/android/graphics/TextLayout.cpp @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2010 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 "TextLayout.h" + +#include <android_runtime/AndroidRuntime.h> + +#include "SkTemplates.h" +#include "unicode/ubidi.h" +#include "unicode/ushape.h" +#include <utils/Log.h> + + +namespace android { +// Returns true if we might need layout. If bidiFlags force LTR, assume no layout, if +// bidiFlags indicate there probably is RTL, assume we do, otherwise scan the text +// looking for a character >= the first RTL character in unicode and assume we do if +// we find one. +bool TextLayout::needsLayout(const jchar* text, jint len, jint bidiFlags) { + if (bidiFlags == kBidi_Force_LTR) { + return false; + } + if ((bidiFlags == kBidi_RTL) || (bidiFlags == kBidi_Default_RTL) || + bidiFlags == kBidi_Force_RTL) { + return true; + } + for (int i = 0; i < len; ++i) { + if (text[i] >= 0x0590) { + return true; + } + } + return false; +} + +/** + * Character-based Arabic shaping. + * + * We'll use harfbuzz and glyph-based shaping instead once we're set up for it. + * + * @context the text context + * @start the start of the text to render + * @count the length of the text to render, start + count must be <= contextCount + * @contextCount the length of the context + * @shaped where to put the shaped text, must have capacity for count uchars + * @return the length of the shaped text, or -1 if error + */ +int TextLayout::shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount, + jchar* shaped, UErrorCode &status) { + jchar buffer[contextCount]; + + // Use fixed length since we need to keep start and count valid + u_shapeArabic(context, contextCount, buffer, contextCount, + U_SHAPE_LENGTH_FIXED_SPACES_NEAR | + U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE | + U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); + + if (U_SUCCESS(status)) { + // trim out 0xffff following ligatures, if any + int end = 0; + for (int i = start, e = start + count; i < e; ++i) { + if (buffer[i] != 0xffff) { + buffer[end++] = buffer[i]; + } + } + count = end; + // LOG(LOG_INFO, "CSRTL", "start %d count %d ccount %d\n", start, count, contextCount); + ubidi_writeReverse(buffer, count, shaped, count, UBIDI_DO_MIRRORING | UBIDI_OUTPUT_REVERSE + | UBIDI_KEEP_BASE_COMBINING, &status); + if (U_SUCCESS(status)) { + return count; + } + } + + return -1; +} + +/** + * Basic character-based layout supporting rtl and arabic shaping. + * Runs bidi on the text and generates a reordered, shaped line in buffer, returning + * the length. + * @text the text + * @len the length of the text in uchars + * @dir receives the resolved paragraph direction + * @buffer the buffer to receive the reordered, shaped line. Must have capacity of + * at least len jchars. + * @flags line bidi flags + * @return the length of the reordered, shaped line, or -1 if error + */ +jint TextLayout::layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer, + UErrorCode &status) { + static const int RTL_OPTS = UBIDI_DO_MIRRORING | UBIDI_KEEP_BASE_COMBINING | + UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_OUTPUT_REVERSE; + + UBiDiLevel bidiReq = 0; + switch (flags) { + case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level + case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level + case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break; + case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break; + case kBidi_Force_LTR: memcpy(buffer, text, len * sizeof(jchar)); return len; + case kBidi_Force_RTL: return shapeRtlText(text, 0, len, len, buffer, status); + } + + int32_t result = -1; + + UBiDi* bidi = ubidi_open(); + if (bidi) { + ubidi_setPara(bidi, text, len, bidiReq, NULL, &status); + if (U_SUCCESS(status)) { + dir = ubidi_getParaLevel(bidi) & 0x1; // 0 if ltr, 1 if rtl + + int rc = ubidi_countRuns(bidi, &status); + if (U_SUCCESS(status)) { + // LOG(LOG_INFO, "LAYOUT", "para bidiReq=%d dir=%d rc=%d\n", bidiReq, dir, rc); + + int32_t slen = 0; + for (int i = 0; i < rc; ++i) { + int32_t start; + int32_t length; + UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &start, &length); + + if (runDir == UBIDI_RTL) { + slen += shapeRtlText(text + start, 0, length, length, buffer + slen, status); + } else { + memcpy(buffer + slen, text + start, length * sizeof(jchar)); + slen += length; + } + } + if (U_SUCCESS(status)) { + result = slen; + } + } + } + ubidi_close(bidi); + } + + return result; +} + +bool TextLayout::prepareText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags, + const jchar** outText, int32_t* outBytes, jchar** outBuffer) { + const jchar *workText = text; + jchar *buffer = NULL; + int dir = kDirection_LTR; + if (needsLayout(text, len, bidiFlags)) { + buffer =(jchar *) malloc(len * sizeof(jchar)); + if (!buffer) { + return false; + } + UErrorCode status = U_ZERO_ERROR; + len = layoutLine(text, len, bidiFlags, dir, buffer, status); // might change len, dir + if (!U_SUCCESS(status)) { + LOG(LOG_WARN, "LAYOUT", "drawText error %d\n", status); + free(buffer); + return false; // can't render + } + + workText = buffer; // use the shaped text + } + + bool trimLeft = false; + bool trimRight = false; + + SkPaint::Align horiz = paint->getTextAlign(); + switch (horiz) { + case SkPaint::kLeft_Align: trimLeft = dir & kDirection_Mask; break; + case SkPaint::kCenter_Align: trimLeft = trimRight = true; break; + case SkPaint::kRight_Align: trimRight = !(dir & kDirection_Mask); + default: break; + } + const jchar* workLimit = workText + len; + + if (trimLeft) { + while (workText < workLimit && *workText == ' ') { + ++workText; + } + } + if (trimRight) { + while (workLimit > workText && *(workLimit - 1) == ' ') { + --workLimit; + } + } + + *outBytes = (workLimit - workText) << 1; + *outText = workText; + *outBuffer = buffer; + + return true; +} + +// Draws or gets the path of a paragraph of text on a single line, running bidi and shaping. +// This will draw if canvas is not null, otherwise path must be non-null and it will create +// a path representing the text that would have been drawn. +void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len, + jint bidiFlags, jfloat x, jfloat y,SkCanvas *canvas, SkPath *path) { + const jchar *workText; + jchar *buffer = NULL; + int32_t workBytes; + if (prepareText(paint, text, len, bidiFlags, &workText, &workBytes, &buffer)) { + SkScalar x_ = SkFloatToScalar(x); + SkScalar y_ = SkFloatToScalar(y); + if (canvas) { + canvas->drawText(workText, workBytes, x_, y_, *paint); + } else { + paint->getTextPath(workText, workBytes, x_, y_, path); + } + free(buffer); + } +} + +bool TextLayout::prepareRtlTextRun(const jchar* context, jsize start, jsize& count, + jsize contextCount, jchar* shaped) { + UErrorCode status = U_ZERO_ERROR; + count = shapeRtlText(context, start, count, contextCount, shaped, status); + if (U_SUCCESS(status)) { + return true; + } else { + LOG(LOG_WARN, "LAYOUT", "drawTextRun error %d\n", status); + } + return false; +} + +void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars, + jint start, jint count, jint contextCount, + int dirFlags, jfloat x, jfloat y, SkCanvas* canvas) { + + SkScalar x_ = SkFloatToScalar(x); + SkScalar y_ = SkFloatToScalar(y); + + uint8_t rtl = dirFlags & 0x1; + if (rtl) { + SkAutoSTMalloc<80, jchar> buffer(contextCount); + if (prepareRtlTextRun(chars, start, count, contextCount, buffer.get())) { + canvas->drawText(buffer.get(), count << 1, x_, y_, *paint); + } + } else { + canvas->drawText(chars + start, count << 1, x_, y_, *paint); + } + } + +void TextLayout::getTextRunAdvances(SkPaint *paint, const jchar *chars, jint start, + jint count, jint contextCount, jint dirFlags, + jfloat *resultAdvances, jfloat &resultTotalAdvance) { + jchar buffer[contextCount]; + + SkScalar* scalarArray = (SkScalar *)resultAdvances; + resultTotalAdvance = 0; + + // this is where we'd call harfbuzz + // for now we just use ushape.c + + int widths; + const jchar* text; + if (dirFlags & 0x1) { // rtl, call arabic shaping in case + UErrorCode status = U_ZERO_ERROR; + // Use fixed length since we need to keep start and count valid + u_shapeArabic(chars, contextCount, buffer, contextCount, + U_SHAPE_LENGTH_FIXED_SPACES_NEAR | + U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE | + U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); + // we shouldn't fail unless there's an out of memory condition, + // in which case we're hosed anyway + for (int i = start, e = i + count; i < e; ++i) { + if (buffer[i] == 0xffff) { + buffer[i] = 0x200b; // zero-width-space for skia + } + } + text = buffer + start; + widths = paint->getTextWidths(text, count << 1, scalarArray); + } else { + text = chars + start; + widths = paint->getTextWidths(text, count << 1, scalarArray); + } + + if (widths < count) { + // Skia operates on code points, not code units, so surrogate pairs return only + // one value. Expand the result so we have one value per UTF-16 code unit. + + // Note, skia's getTextWidth gets confused if it encounters a surrogate pair, + // leaving the remaining widths zero. Not nice. + for (int i = 0, p = 0; i < widths; ++i) { + resultTotalAdvance += resultAdvances[p++] = SkScalarToFloat(scalarArray[i]); + if (p < count && text[p] >= 0xdc00 && text[p] < 0xe000 && + text[p-1] >= 0xd800 && text[p-1] < 0xdc00) { + resultAdvances[p++] = 0; + } + } + } else { + for (int i = 0; i < count; i++) { + resultTotalAdvance += resultAdvances[i] = SkScalarToFloat(scalarArray[i]); + } + } +} + + +// Draws a paragraph of text on a single line, running bidi and shaping +void TextLayout::drawText(SkPaint* paint, const jchar* text, jsize len, + int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas) { + + handleText(paint, text, len, bidiFlags, x, y, canvas, NULL); +} + +void TextLayout::getTextPath(SkPaint *paint, const jchar *text, jsize len, + jint bidiFlags, jfloat x, jfloat y, SkPath *path) { + handleText(paint, text, len, bidiFlags, x, y, NULL, path); +} + + +void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count, + int bidiFlags, jfloat hOffset, jfloat vOffset, + SkPath* path, SkCanvas* canvas) { + + SkScalar h_ = SkFloatToScalar(hOffset); + SkScalar v_ = SkFloatToScalar(vOffset); + + if (!needsLayout(text, count, bidiFlags)) { + canvas->drawTextOnPathHV(text, count << 1, *path, h_, v_, *paint); + return; + } + + SkAutoSTMalloc<80, jchar> buffer(count); + int dir = kDirection_LTR; + UErrorCode status = U_ZERO_ERROR; + count = layoutLine(text, count, bidiFlags, dir, buffer.get(), status); + if (U_SUCCESS(status)) { + canvas->drawTextOnPathHV(buffer.get(), count << 1, *path, h_, v_, *paint); + } +} + +} diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h new file mode 100644 index 0000000..8f666c0 --- /dev/null +++ b/core/jni/android/graphics/TextLayout.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010 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 "SkCanvas.h" +#include "SkPaint.h" +#include "unicode/utypes.h" + +namespace android { + +class TextLayout { +public: + + enum { + kDirection_LTR = 0, + kDirection_RTL = 1, + + kDirection_Mask = 0x1 + }; + + enum { + kBidi_LTR = 0, + kBidi_RTL = 1, + kBidi_Default_LTR = 2, + kBidi_Default_RTL = 3, + kBidi_Force_LTR = 4, + kBidi_Force_RTL = 5, + + kBidi_Mask = 0x7 + }; + + /* + * Draws a unidirectional run of text. + */ + static void drawTextRun(SkPaint* paint, const jchar* chars, + jint start, jint count, jint contextCount, + int dirFlags, jfloat x, jfloat y, SkCanvas* canvas); + + static void getTextRunAdvances(SkPaint *paint, const jchar *chars, jint start, + jint count, jint contextCount, jint dirFlags, + jfloat *resultAdvances, jfloat &resultTotalAdvance); + + static void drawText(SkPaint* paint, const jchar* text, jsize len, + jint bidiFlags, jfloat x, jfloat y, SkCanvas* canvas); + + static void getTextPath(SkPaint *paint, const jchar *text, jsize len, + jint bidiFlags, jfloat x, jfloat y, SkPath *path); + + static void drawTextOnPath(SkPaint* paint, const jchar* text, jsize len, + int bidiFlags, jfloat hOffset, jfloat vOffset, + SkPath* path, SkCanvas* canvas); + + static bool prepareText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags, + const jchar** outText, int32_t* outBytes, jchar** outBuffer); + static bool prepareRtlTextRun(const jchar* context, jsize start, jsize& count, + jsize contextCount, jchar* shaped); + + +private: + static bool needsLayout(const jchar* text, jint len, jint bidiFlags); + static int shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount, + jchar* shaped, UErrorCode &status); + static jint layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer, + UErrorCode &status); + static void handleText(SkPaint *paint, const jchar* text, jsize len, + int bidiFlags, jfloat x, jfloat y,SkCanvas *canvas, SkPath *path); +}; + +} diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp index 7c7bfeb..1fe72e6 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/core/jni/android/graphics/Typeface.cpp @@ -130,7 +130,13 @@ static SkTypeface* Typeface_createFromAsset(JNIEnv* env, jobject, return NULL; } - return SkTypeface::CreateFromStream(new AssetStream(asset, true)); + SkStream* stream = new AssetStream(asset, true); + SkTypeface* face = SkTypeface::CreateFromStream(stream); + // SkTypeFace::CreateFromStream calls ref() on the stream, so we + // need to unref it here or it won't be freed later on + stream->unref(); + + return face; } static SkTypeface* Typeface_createFromFile(JNIEnv* env, jobject, jstring jpath) { diff --git a/core/jni/android_bluetooth_ScoSocket.cpp b/core/jni/android_bluetooth_ScoSocket.cpp deleted file mode 100644 index 94e4409..0000000 --- a/core/jni/android_bluetooth_ScoSocket.cpp +++ /dev/null @@ -1,689 +0,0 @@ -/* -** 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> -#include <bluetooth/hci.h> - -#define MAX_LINE 255 - -/* - * Defines the module strings used in the blacklist file. - * These are used by consumers of the blacklist file to see if the line is - * used by that module. - */ -#define SCO_BLACKLIST_MODULE_NAME "scoSocket" - - -/* Define the type strings used in the blacklist file. */ -#define BLACKLIST_BY_NAME "name" -#define BLACKLIST_BY_PARTIAL_NAME "partial_name" -#define BLACKLIST_BY_OUI "vendor_oui" - -#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, uint16_t sco_pkt_type); -static int accept_work(int signal_sk); -static void wait_for_close(int sk, int signal_sk); -static void closeNative(JNIEnv *env, jobject object); - -static void parseBlacklist(void); -static uint16_t getScoType(char *address, const char *name); - -#define COMPARE_STRING(key, s) (!strncmp(key, s, strlen(s))) - -/* Blacklist data */ -typedef struct scoBlacklist { - int fieldType; - char *value; - uint16_t scoType; - struct scoBlacklist *next; -} scoBlacklist_t; - -#define BL_TYPE_NAME 1 // Field type is name string - -static scoBlacklist_t *blacklist = NULL; - -/* 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 - uint16_t sco_pkt_type; // SCO packet types supported -}; - -static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { - return (native_data_t *)(env->GetIntField(object, field_mNativeData)); -} - -static uint16_t str2scoType (char *key) { - LOGV("%s: key = %s", __FUNCTION__, key); - if (COMPARE_STRING(key, "ESCO_HV1")) - return ESCO_HV1; - if (COMPARE_STRING(key, "ESCO_HV2")) - return ESCO_HV2; - if (COMPARE_STRING(key, "ESCO_HV3")) - return ESCO_HV3; - if (COMPARE_STRING(key, "ESCO_EV3")) - return ESCO_EV3; - if (COMPARE_STRING(key, "ESCO_EV4")) - return ESCO_EV4; - if (COMPARE_STRING(key, "ESCO_EV5")) - return ESCO_EV5; - if (COMPARE_STRING(key, "ESCO_2EV3")) - return ESCO_2EV3; - if (COMPARE_STRING(key, "ESCO_3EV3")) - return ESCO_3EV3; - if (COMPARE_STRING(key, "ESCO_2EV5")) - return ESCO_2EV5; - if (COMPARE_STRING(key, "ESCO_3EV5")) - return ESCO_3EV5; - if (COMPARE_STRING(key, "SCO_ESCO_MASK")) - return SCO_ESCO_MASK; - if (COMPARE_STRING(key, "EDR_ESCO_MASK")) - return EDR_ESCO_MASK; - if (COMPARE_STRING(key, "ALL_ESCO_MASK")) - return ALL_ESCO_MASK; - LOGE("Unknown SCO Type (%s) skipping",key); - return 0; -} - -static void parseBlacklist(void) { - const char *filename = "/etc/bluetooth/blacklist.conf"; - char line[MAX_LINE]; - scoBlacklist_t *list = NULL; - scoBlacklist_t *newelem; - - LOGV(__FUNCTION__); - - /* Open file */ - FILE *fp = fopen(filename, "r"); - if(!fp) { - LOGE("Error(%s)opening blacklist file", strerror(errno)); - return; - } - - while (fgets(line, MAX_LINE, fp) != NULL) { - if ((COMPARE_STRING(line, "//")) || (!strcmp(line, ""))) - continue; - char *module = strtok(line,":"); - if (COMPARE_STRING(module, SCO_BLACKLIST_MODULE_NAME)) { - newelem = (scoBlacklist_t *)calloc(1, sizeof(scoBlacklist_t)); - if (newelem == NULL) { - LOGE("%s: out of memory!", __FUNCTION__); - return; - } - // parse line - char *type = strtok(NULL, ","); - char *valueList = strtok(NULL, ","); - char *paramList = strtok(NULL, ","); - if (COMPARE_STRING(type, BLACKLIST_BY_NAME)) { - // Extract Name from Value list - newelem->fieldType = BL_TYPE_NAME; - newelem->value = (char *)calloc(1, strlen(valueList)); - if (newelem->value == NULL) { - LOGE("%s: out of memory!", __FUNCTION__); - continue; - } - valueList++; // Skip open quote - strncpy(newelem->value, valueList, strlen(valueList) - 1); - - // Get Sco Settings from Parameters - char *param = strtok(paramList, ";"); - uint16_t scoTypes = 0; - while (param != NULL) { - uint16_t sco; - if (param[0] == '-') { - param++; - sco = str2scoType(param); - if (sco != 0) - scoTypes &= ~sco; - } else if (param[0] == '+') { - param++; - sco = str2scoType(param); - if (sco != 0) - scoTypes |= sco; - } else if (param[0] == '=') { - param++; - sco = str2scoType(param); - if (sco != 0) - scoTypes = sco; - } else { - LOGE("Invalid SCO type must be =, + or -"); - } - param = strtok(NULL, ";"); - } - newelem->scoType = scoTypes; - } else { - LOGE("Unknown SCO type entry in Blacklist file"); - continue; - } - if (list) { - list->next = newelem; - list = newelem; - } else { - blacklist = list = newelem; - } - LOGI("Entry name = %s ScoTypes = 0x%x", newelem->value, - newelem->scoType); - } - } - fclose(fp); - return; -} -static uint16_t getScoType(char *address, const char *name) { - uint16_t ret = 0; - scoBlacklist_t *list = blacklist; - - while (list != NULL) { - if (list->fieldType == BL_TYPE_NAME) { - if (COMPARE_STRING(name, list->value)) { - ret = list->scoType; - break; - } - } - list = list->next; - } - LOGI("%s %s - 0x%x", __FUNCTION__, name, ret); - return ret; -} -#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"); - - /* Read the blacklist file in here */ - parseBlacklist(); -#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, - jstring name) { - - 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; - const char *c_name; - - 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 (name == NULL) { - LOGE("%s: Null pointer passed in for device name", __FUNCTION__); - data->sco_pkt_type = 0; - } else { - c_name = env->GetStringUTFChars(name, NULL); - /* See if this device is in the black list */ - data->sco_pkt_type = getScoType(data->address, c_name); - env->ReleaseStringUTFChars(name, c_name); - } - 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, data->sco_pkt_type); - } - - /* 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, uint16_t sco_pkt_type) { - 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); - addr.sco_pkt_type = sco_pkt_type; - 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;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 index 43c3a95..aae0f21 100644 --- a/core/jni/android_bluetooth_common.cpp +++ b/core/jni/android_bluetooth_common.cpp @@ -69,6 +69,16 @@ static Properties adapter_properties[] = { {"UUIDs", DBUS_TYPE_ARRAY}, }; +static Properties input_properties[] = { + {"Connected", DBUS_TYPE_BOOLEAN}, +}; + +static Properties pan_properties[] = { + {"Connected", DBUS_TYPE_BOOLEAN}, + {"Interface", DBUS_TYPE_STRING}, + {"UUID", DBUS_TYPE_STRING}, +}; + typedef union { char *str_val; int int_val; @@ -187,6 +197,7 @@ dbus_bool_t dbus_func_args_async(JNIEnv *env, 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, nat, @@ -699,6 +710,16 @@ jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg) sizeof(remote_device_properties) / sizeof(Properties)); } +jobjectArray parse_input_property_change(JNIEnv *env, DBusMessage *msg) { + return parse_property_change(env, msg, (Properties *) &input_properties, + sizeof(input_properties) / sizeof(Properties)); +} + +jobjectArray parse_pan_property_change(JNIEnv *env, DBusMessage *msg) { + return parse_property_change(env, msg, (Properties *) &pan_properties, + sizeof(pan_properties) / sizeof(Properties)); +} + jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter) { return parse_properties(env, iter, (Properties *) &adapter_properties, sizeof(adapter_properties) / sizeof(Properties)); @@ -709,6 +730,11 @@ jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter) sizeof(remote_device_properties) / sizeof(Properties)); } +jobjectArray parse_input_properties(JNIEnv *env, DBusMessageIter *iter) { + return parse_properties(env, iter, (Properties *) &input_properties, + sizeof(input_properties) / sizeof(Properties)); +} + int get_bdaddr(const char *str, bdaddr_t *ba) { char *d = ((char *)ba) + 5, *endp; int i; diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h index 378bb6f..9222e1a 100644 --- a/core/jni/android_bluetooth_common.h +++ b/core/jni/android_bluetooth_common.h @@ -162,6 +162,9 @@ jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter); jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter); jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg); jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg); +jobjectArray parse_input_properties(JNIEnv *env, DBusMessageIter *iter); +jobjectArray parse_input_property_change(JNIEnv *env, DBusMessage *msg); +jobjectArray parse_pan_property_change(JNIEnv *env, DBusMessage *msg); void append_variant(DBusMessageIter *iter, int type, void *val); int get_bdaddr(const char *str, bdaddr_t *ba); void get_bdaddr_as_string(const bdaddr_t *ba, char *str); diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index 91449bc..040dac3 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -29,7 +29,7 @@ #include <string.h> #include <unistd.h> -#include "CursorWindow.h" +#include "binder/CursorWindow.h" #include "sqlite3_exception.h" #include "android_util_Binder.h" @@ -225,70 +225,6 @@ LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); 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 or null for %d,%d from %p", row, column, window); - - field_slot_t field; - err = window->read_field_slot(row, column, &field); - if (err != 0) { - throwExceptionWithRowCol(env, row, column); - return NULL; - } - - return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL; -} - -static jboolean isString_native(JNIEnv* env, jobject object, jint row, jint column) -{ - int32_t err; - CursorWindow * window = GET_WINDOW(env, object); -LOG_WINDOW("Checking if column is a string or null for %d,%d from %p", row, column, window); - - field_slot_t field; - err = window->read_field_slot(row, column, &field); - if (err != 0) { - throwExceptionWithRowCol(env, row, column); - return NULL; - } - - return field.type == FIELD_TYPE_STRING || field.type == FIELD_TYPE_NULL; -} - -static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column) -{ - int32_t err; - CursorWindow * window = GET_WINDOW(env, object); -LOG_WINDOW("Checking if column is an integer for %d,%d from %p", row, column, window); - - field_slot_t field; - err = window->read_field_slot(row, column, &field); - if (err != 0) { - throwExceptionWithRowCol(env, row, column); - return NULL; - } - - return field.type == FIELD_TYPE_INTEGER; -} - -static jboolean isFloat_native(JNIEnv* env, jobject object, jint row, jint column) -{ - int32_t err; - CursorWindow * window = GET_WINDOW(env, object); -LOG_WINDOW("Checking if column is a float for %d,%d from %p", row, column, window); - - field_slot_t field; - err = window->read_field_slot(row, column, &field); - if (err != 0) { - throwExceptionWithRowCol(env, row, column); - return NULL; - } - - return field.type == FIELD_TYPE_FLOAT; -} - static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column) { int32_t err; @@ -487,10 +423,9 @@ LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); } } -static jboolean isNull_native(JNIEnv* env, jobject object, jint row, jint column) +bool isNull_native(CursorWindow *window, jint row, jint column) { - CursorWindow * window = GET_WINDOW(env, object); -LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window); + LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window); bool isNull; if (window->getNull(row, column, &isNull)) { @@ -652,6 +587,26 @@ static void freeLastRow(JNIEnv * env, jobject object) { window->freeLastRow(); } +static jint getType_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); + LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window); + + if (isNull_native(window, row, column)) { + return FIELD_TYPE_NULL; + } + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } + + return field.type; +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ @@ -662,11 +617,9 @@ static JNINativeMethod sMethods[] = {"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}, @@ -676,9 +629,7 @@ static JNINativeMethod sMethods[] = {"putDouble_native", "(DII)Z", (void *)putDouble_native}, {"freeLastRow_native", "()V", (void *)freeLastRow}, {"putNull_native", "(II)Z", (void *)putNull_native}, - {"isString_native", "(II)Z", (void *)isString_native}, - {"isFloat_native", "(II)Z", (void *)isFloat_native}, - {"isInteger_native", "(II)Z", (void *)isInteger_native}, + {"getType_native", "(II)I", (void *)getType_native}, }; int register_android_database_CursorWindow(JNIEnv * env) diff --git a/core/jni/android_database_SQLiteCompiledSql.cpp b/core/jni/android_database_SQLiteCompiledSql.cpp index 8d1c39e..de4c5c8 100644 --- a/core/jni/android_database_SQLiteCompiledSql.cpp +++ b/core/jni/android_database_SQLiteCompiledSql.cpp @@ -91,22 +91,11 @@ static void native_compile(JNIEnv* env, jobject object, jstring sqlString) compile(env, object, GET_HANDLE(env, object), sqlString); } -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_finalize", "()V", (void *)native_finalize}, }; int register_android_database_SQLiteCompiledSql(JNIEnv * env) diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp index 36234a9..36e9089 100644 --- a/core/jni/android_database_SQLiteDatabase.cpp +++ b/core/jni/android_database_SQLiteDatabase.cpp @@ -15,7 +15,7 @@ */ #undef LOG_TAG -#define LOG_TAG "Database" +#define LOG_TAG "SqliteDatabaseCpp" #include <utils/Log.h> #include <utils/String8.h> @@ -62,9 +62,11 @@ enum { }; static jfieldID offset_db_handle; +static jmethodID method_custom_function_callback; +static jclass string_class = NULL; -static char *createStr(const char *path) { - int len = strlen(path); +static char *createStr(const char *path, short extra) { + int len = strlen(path) + extra; char *str = (char *)malloc(len + 1); strncpy(str, path, len); str[len] = NULL; @@ -85,7 +87,7 @@ static void registerLoggingFunc(const char *path) { } LOGV("Registering sqlite logging func \n"); - int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path)); + int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path, 0)); if (err != SQLITE_OK) { LOGE("sqlite_config failed error_code = %d. THIS SHOULD NEVER occur.\n", err); return; @@ -176,13 +178,17 @@ done: if (handle != NULL) sqlite3_close(handle); } -static char *getDatabaseName(JNIEnv* env, sqlite3 * handle, jstring databaseName) { +static char *getDatabaseName(JNIEnv* env, sqlite3 * handle, jstring databaseName, short connNum) { char const *path = env->GetStringUTFChars(databaseName, NULL); if (path == NULL) { LOGE("Failure in getDatabaseName(). VM ran out of memory?\n"); return NULL; // VM would have thrown OutOfMemoryError } - char *dbNameStr = createStr(path); + char *dbNameStr = createStr(path, 4); + if (connNum > 999) { // TODO: if number of pooled connections > 999, fix this line. + connNum = -1; + } + sprintf(dbNameStr + strlen(path), "|%03d", connNum); env->ReleaseStringUTFChars(databaseName, path); return dbNameStr; } @@ -192,10 +198,10 @@ static void sqlTrace(void *databaseName, const char *sql) { } /* public native void enableSqlTracing(); */ -static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName) +static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName, jshort connType) { sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); - sqlite3_trace(handle, &sqlTrace, (void *)getDatabaseName(env, handle, databaseName)); + sqlite3_trace(handle, &sqlTrace, (void *)getDatabaseName(env, handle, databaseName, connType)); } static void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) { @@ -204,13 +210,13 @@ static void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) { } /* public native void enableSqlProfiling(); */ -static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName) +static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName, jshort connType) { sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); - sqlite3_profile(handle, &sqlProfile, (void *)getDatabaseName(env, handle, databaseName)); + sqlite3_profile(handle, &sqlProfile, (void *)getDatabaseName(env, handle, databaseName, + connType)); } - /* public native void close(); */ static void dbclose(JNIEnv* env, jobject object) { @@ -240,73 +246,6 @@ static void dbclose(JNIEnv* env, jobject object) } } -/* 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 -#ifndef DB_LOG_STATEMENTS - IF_LOGV() -#endif - { - 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); -} - /* native int native_getDbLookaside(); */ static jint native_getDbLookaside(JNIEnv* env, jobject object) { @@ -443,19 +382,93 @@ static jint native_releaseMemory(JNIEnv *env, jobject clazz) return sqlite3_release_memory(SQLITE_SOFT_HEAP_LIMIT); } +static void native_finalize(JNIEnv* env, jobject object, jint statementId) +{ + if (statementId > 0) { + sqlite3_finalize((sqlite3_stmt *)statementId); + } +} + +static void custom_function_callback(sqlite3_context * context, int argc, sqlite3_value ** argv) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (!env) { + LOGE("custom_function_callback cannot call into Java on this thread"); + return; + } + // get global ref to CustomFunction object from our user data + jobject function = (jobject)sqlite3_user_data(context); + + // pack up the arguments into a string array + if (!string_class) + string_class = (jclass)env->NewGlobalRef(env->FindClass("java/lang/String")); + jobjectArray strArray = env->NewObjectArray(argc, string_class, NULL); + if (!strArray) + goto done; + for (int i = 0; i < argc; i++) { + char* arg = (char *)sqlite3_value_text(argv[i]); + if (!arg) { + LOGE("NULL argument in custom_function_callback. This should not happen."); + return; + } + jobject obj = env->NewStringUTF(arg); + if (!obj) + goto done; + env->SetObjectArrayElement(strArray, i, obj); + env->DeleteLocalRef(obj); + } + + env->CallVoidMethod(function, method_custom_function_callback, strArray); + +done: + if (env->ExceptionCheck()) { + LOGE("An exception was thrown by custom sqlite3 function."); + LOGE_EX(env); + env->ExceptionClear(); + } +} + +static jint native_addCustomFunction(JNIEnv* env, jobject object, + jstring name, jint numArgs, jobject function) +{ + sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); + char const *nameStr = env->GetStringUTFChars(name, NULL); + jobject ref = env->NewGlobalRef(function); + LOGD("native_addCustomFunction %s ref: %p", nameStr, ref); + int err = sqlite3_create_function(handle, nameStr, numArgs, SQLITE_UTF8, + (void *)ref, custom_function_callback, NULL, NULL); + env->ReleaseStringUTFChars(name, nameStr); + + if (err == SQLITE_OK) + return (int)ref; + else { + LOGE("sqlite3_create_function returned %d", err); + env->DeleteGlobalRef(ref); + throw_sqlite3_exception(env, handle); + return 0; + } +} + +static void native_releaseCustomFunction(JNIEnv* env, jobject object, jint ref) +{ + LOGD("native_releaseCustomFunction %d", ref); + env->DeleteGlobalRef((jobject)ref); +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen}, {"dbclose", "()V", (void *)dbclose}, - {"enableSqlTracing", "(Ljava/lang/String;)V", (void *)enableSqlTracing}, - {"enableSqlProfiling", "(Ljava/lang/String;)V", (void *)enableSqlProfiling}, - {"native_execSQL", "(Ljava/lang/String;)V", (void *)native_execSQL}, - {"lastInsertRow", "()J", (void *)lastInsertRow}, - {"lastChangeCount", "()I", (void *)lastChangeCount}, + {"enableSqlTracing", "(Ljava/lang/String;S)V", (void *)enableSqlTracing}, + {"enableSqlProfiling", "(Ljava/lang/String;S)V", (void *)enableSqlProfiling}, {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale}, {"native_getDbLookaside", "()I", (void *)native_getDbLookaside}, {"releaseMemory", "()I", (void *)native_releaseMemory}, + {"native_finalize", "(I)V", (void *)native_finalize}, + {"native_addCustomFunction", + "(Ljava/lang/String;ILandroid/database/sqlite/SQLiteDatabase$CustomFunction;)I", + (void *)native_addCustomFunction}, + {"native_releaseCustomFunction", "(I)V", (void *)native_releaseCustomFunction}, }; int register_android_database_SQLiteDatabase(JNIEnv *env) @@ -474,7 +487,19 @@ int register_android_database_SQLiteDatabase(JNIEnv *env) return -1; } - return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase", sMethods, NELEM(sMethods)); + clazz = env->FindClass("android/database/sqlite/SQLiteDatabase$CustomFunction"); + if (clazz == NULL) { + LOGE("Can't find android/database/sqlite/SQLiteDatabase$CustomFunction\n"); + return -1; + } + method_custom_function_callback = env->GetMethodID(clazz, "callback", "([Ljava/lang/String;)V"); + if (method_custom_function_callback == NULL) { + LOGE("Can't find method SQLiteDatabase.CustomFunction.callback\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 */ @@ -523,6 +548,7 @@ void throw_sqlite3_exception(JNIEnv* env, int errcode, exceptionClass = "android/database/sqlite/SQLiteDiskIOException"; break; case SQLITE_CORRUPT: + case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException"; break; case SQLITE_CONSTRAINT: @@ -540,6 +566,36 @@ void throw_sqlite3_exception(JNIEnv* env, int errcode, case SQLITE_MISUSE: exceptionClass = "android/database/sqlite/SQLiteMisuseException"; break; + case SQLITE_PERM: + exceptionClass = "android/database/sqlite/SQLiteAccessPermException"; + break; + case SQLITE_BUSY: + exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException"; + break; + case SQLITE_LOCKED: + exceptionClass = "android/database/sqlite/SQLiteTableLockedException"; + break; + case SQLITE_READONLY: + exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException"; + break; + case SQLITE_CANTOPEN: + exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException"; + break; + case SQLITE_TOOBIG: + exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException"; + break; + case SQLITE_RANGE: + exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException"; + break; + case SQLITE_NOMEM: + exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException"; + break; + case SQLITE_MISMATCH: + exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException"; + break; + case SQLITE_UNCLOSED: + exceptionClass = "android/database/sqlite/SQLiteUnfinalizedObjectsException"; + break; default: exceptionClass = "android/database/sqlite/SQLiteException"; break; diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp index 4427168..e383123 100644 --- a/core/jni/android_database_SQLiteQuery.cpp +++ b/core/jni/android_database_SQLiteQuery.cpp @@ -29,7 +29,7 @@ #include <string.h> #include <unistd.h> -#include "CursorWindow.h" +#include "binder/CursorWindow.h" #include "sqlite3_exception.h" @@ -205,7 +205,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, int offset = window->alloc(size); if (!offset) { window->freeLastRow(); - LOGE("Failed allocating %u bytes for text/blob at %d,%d", size, + LOGD("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; } @@ -225,7 +225,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, 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); + LOGD("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); @@ -234,7 +234,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, 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); + LOGD("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); @@ -245,7 +245,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, int offset = window->alloc(size); if (!offset) { window->freeLastRow(); - LOGE("Failed allocating %u bytes for blob at %d,%d", size, + LOGD("Failed allocating %u bytes for blob at %d,%d", size, startPos + numRows, i); return startPos + numRows + finish_program_and_get_row_count(statement) + 1; } diff --git a/core/jni/android_database_SQLiteStatement.cpp b/core/jni/android_database_SQLiteStatement.cpp index ff2ed5d..0f3114b 100644 --- a/core/jni/android_database_SQLiteStatement.cpp +++ b/core/jni/android_database_SQLiteStatement.cpp @@ -16,7 +16,9 @@ */ #undef LOG_TAG -#define LOG_TAG "Cursor" +#define LOG_TAG "SQLiteStatementCpp" + +#include "android_util_Binder.h" #include <jni.h> #include <JNIHelp.h> @@ -24,11 +26,16 @@ #include <sqlite3.h> +#include <cutils/ashmem.h> #include <utils/Log.h> +#include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> #include "sqlite3_exception.h" @@ -48,22 +55,40 @@ static jfieldID gStatementField; (sqlite3 *)env->GetIntField(object, gHandleField) -static void native_execute(JNIEnv* env, jobject object) +static jint native_execute(JNIEnv* env, jobject object) { int err; sqlite3 * handle = GET_HANDLE(env, object); sqlite3_stmt * statement = GET_STATEMENT(env, object); + int numChanges = -1; // Execute the statement err = sqlite3_step(statement); - // Throw an exception if an error occured - if (err != SQLITE_DONE) { + // Throw an exception if an error occurred + if (err == SQLITE_ROW) { + throw_sqlite3_exception(env, + "Queries can be performed using SQLiteDatabase query or rawQuery methods only."); + } else if (err != SQLITE_DONE) { throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle)); + } else { + numChanges = sqlite3_changes(handle); } - // Reset the statment so it's ready to use again + // Reset the statement so it's ready to use again sqlite3_reset(statement); + return numChanges; +} + +static jlong native_executeInsert(JNIEnv* env, jobject object) +{ + sqlite3 * handle = GET_HANDLE(env, object); + jint numChanges = native_execute(env, object); + if (numChanges > 0) { + return sqlite3_last_insert_rowid(handle); + } else { + return -1; + } } static jlong native_1x1_long(JNIEnv* env, jobject object) @@ -115,13 +140,113 @@ static jstring native_1x1_string(JNIEnv* env, jobject object) return value; } +static jobject createParcelFileDescriptor(JNIEnv * env, int fd) +{ + // Create FileDescriptor object + jobject fileDesc = newFileDescriptor(env, fd); + if (fileDesc == NULL) { + // FileDescriptor constructor has thrown an exception + close(fd); + return NULL; + } + + // Wrap it in a ParcelFileDescriptor + jobject parcelFileDesc = newParcelFileDescriptor(env, fileDesc); + if (parcelFileDesc == NULL) { + // ParcelFileDescriptor constructor has thrown an exception + close(fd); + return NULL; + } + + return parcelFileDesc; +} + +// Creates an ashmem area, copies some data into it, and returns +// a ParcelFileDescriptor for the ashmem area. +static jobject create_ashmem_region_with_data(JNIEnv * env, + const void * data, int length) +{ + // Create ashmem area + int fd = ashmem_create_region(NULL, length); + if (fd < 0) { + LOGE("ashmem_create_region failed: %s", strerror(errno)); + jniThrowIOException(env, errno); + return NULL; + } + + if (length > 0) { + // mmap the ashmem area + void * ashmem_ptr = + mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (ashmem_ptr == MAP_FAILED) { + LOGE("mmap failed: %s", strerror(errno)); + jniThrowIOException(env, errno); + close(fd); + return NULL; + } + + // Copy data to ashmem area + memcpy(ashmem_ptr, data, length); + + // munmap ashmem area + if (munmap(ashmem_ptr, length) < 0) { + LOGE("munmap failed: %s", strerror(errno)); + jniThrowIOException(env, errno); + close(fd); + return NULL; + } + } + + // Make ashmem area read-only + if (ashmem_set_prot_region(fd, PROT_READ) < 0) { + LOGE("ashmem_set_prot_region failed: %s", strerror(errno)); + jniThrowIOException(env, errno); + close(fd); + return NULL; + } + + // Wrap it in a ParcelFileDescriptor + return createParcelFileDescriptor(env, fd); +} + +static jobject native_1x1_blob_ashmem(JNIEnv* env, jobject object) +{ + int err; + sqlite3 * handle = GET_HANDLE(env, object); + sqlite3_stmt * statement = GET_STATEMENT(env, object); + jobject value = NULL; + + // Execute the statement + err = sqlite3_step(statement); + + // Handle the result + if (err == SQLITE_ROW) { + // No errors, read the data and return it + const void * blob = sqlite3_column_blob(statement, 0); + if (blob != NULL) { + int len = sqlite3_column_bytes(statement, 0); + if (len >= 0) { + value = create_ashmem_region_with_data(env, blob, len); + } + } + } else { + throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle)); + } + + // 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_execute", "()I", (void *)native_execute}, + {"native_executeInsert", "()J", (void *)native_executeInsert}, {"native_1x1_long", "()J", (void *)native_1x1_long}, {"native_1x1_string", "()Ljava/lang/String;", (void *)native_1x1_string}, + {"native_1x1_blob_ashmem", "()Landroid/os/ParcelFileDescriptor;", (void *)native_1x1_blob_ashmem}, }; int register_android_database_SQLiteStatement(JNIEnv * env) diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 6575b9a..351f264 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -224,7 +224,8 @@ static JNINativeMethod gNetworkUtilMethods[] = { { "addRoute", "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)I", (void *)android_net_utils_addRoute }, { "removeHostRoutes", "(Ljava/lang/String;)I", (void *)android_net_utils_removeHostRoutes }, - { "getDefaultRoute", "(Ljava/lang/String;)I", (void *)android_net_utils_getDefaultRoute }, + { "getDefaultRouteNative", "(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 }, diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index fb029e6..c5ccc43 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -98,6 +98,11 @@ static jstring doStringCommand(JNIEnv *env, const char *cmd) } } +static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jobject clazz) +{ + return (jboolean)(::is_wifi_driver_loaded() == 1); +} + static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject clazz) { return (jboolean)(::wifi_load_driver() == 0); @@ -406,6 +411,30 @@ static jint android_net_wifi_getPowerModeCommand(JNIEnv* env, jobject clazz) return (jint)power; } +static jboolean android_net_wifi_setBandCommand(JNIEnv* env, jobject clazz, jint band) +{ + char cmdstr[25]; + + int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETBAND %d", band); + int cmdTooLong = numWritten >= (int)sizeof(cmdstr); + + return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK"); +} + +static jint android_net_wifi_getBandCommand(JNIEnv* env, jobject clazz) +{ + char reply[25]; + int band; + + if (doCommand("DRIVER GETBAND", reply, sizeof(reply)) != 0) { + return (jint)-1; + } + // reply comes back in the form "Band X" where X is the + // number we're interested in. + sscanf(reply, "%*s %u", &band); + return (jint)band; +} + static jboolean android_net_wifi_setNumAllowedChannelsCommand(JNIEnv* env, jobject clazz, jint numChannels) { char cmdstr[256]; @@ -493,15 +522,6 @@ static jboolean android_net_wifi_clearBlacklistCommand(JNIEnv* env, jobject claz return doBooleanCommand("BLACKLIST clear", "OK"); } -static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject clazz, jboolean enabled) -{ - char cmdstr[25]; - - snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETSUSPENDOPT %d", enabled ? 0 : 1); - return doBooleanCommand(cmdstr, "OK"); -} - - static jboolean android_net_wifi_doDhcpRequest(JNIEnv* env, jobject clazz, jobject info) { jint ipaddr, gateway, mask, dns1, dns2, server, lease; @@ -533,6 +553,7 @@ static JNINativeMethod gWifiMethods[] = { /* name, signature, funcPtr */ { "loadDriver", "()Z", (void *)android_net_wifi_loadDriver }, + { "isDriverLoaded", "()Z", (void *)android_net_wifi_isDriverLoaded}, { "unloadDriver", "()Z", (void *)android_net_wifi_unloadDriver }, { "startSupplicant", "()Z", (void *)android_net_wifi_startSupplicant }, { "stopSupplicant", "()Z", (void *)android_net_wifi_stopSupplicant }, @@ -564,6 +585,8 @@ static JNINativeMethod gWifiMethods[] = { { "stopPacketFiltering", "()Z", (void*) android_net_wifi_stopPacketFiltering }, { "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand }, { "getPowerModeCommand", "()I", (void*) android_net_wifi_getPowerModeCommand }, + { "setBandCommand", "(I)Z", (void*) android_net_wifi_setBandCommand}, + { "getBandCommand", "()I", (void*) android_net_wifi_getBandCommand}, { "setNumAllowedChannelsCommand", "(I)Z", (void*) android_net_wifi_setNumAllowedChannelsCommand }, { "getNumAllowedChannelsCommand", "()I", (void*) android_net_wifi_getNumAllowedChannelsCommand }, { "setBluetoothCoexistenceModeCommand", "(I)Z", @@ -580,7 +603,6 @@ static JNINativeMethod gWifiMethods[] = { { "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 }, - { "setSuspendOptimizationsCommand", "(Z)Z", (void*) android_net_wifi_setSuspendOptimizationsCommand}, { "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest }, { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError }, diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 3ee404a..4a877d2 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define LOG_TAG "android.os.Debug" #include "JNIHelp.h" #include "jni.h" #include "utils/misc.h" @@ -24,6 +25,8 @@ #include <unistd.h> #include <time.h> #include <sys/time.h> +#include <errno.h> +#include <assert.h> #ifdef HAVE_MALLOC_H #include <malloc.h> @@ -274,6 +277,176 @@ 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); + +#ifdef HAVE_ANDROID_OS +/* pulled out of bionic */ +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); +#define SIZE_FLAG_ZYGOTE_CHILD (1<<31) +#define BACKTRACE_SIZE 32 + +/* + * This is a qsort() callback. + * + * See dumpNativeHeap() for comments about the data format and sort order. + */ +static int compareHeapRecords(const void* vrec1, const void* vrec2) +{ + const size_t* rec1 = (const size_t*) vrec1; + const size_t* rec2 = (const size_t*) vrec2; + size_t size1 = *rec1; + size_t size2 = *rec2; + + if (size1 < size2) { + return 1; + } else if (size1 > size2) { + return -1; + } + + intptr_t* bt1 = (intptr_t*)(rec1 + 2); + intptr_t* bt2 = (intptr_t*)(rec2 + 2); + for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) { + intptr_t addr1 = bt1[idx]; + intptr_t addr2 = bt2[idx]; + if (addr1 == addr2) { + if (addr1 == 0) + break; + continue; + } + if (addr1 < addr2) { + return -1; + } else if (addr1 > addr2) { + return 1; + } + } + + return 0; +} + +/* + * The get_malloc_leak_info() call returns an array of structs that + * look like this: + * + * size_t size + * size_t allocations + * intptr_t backtrace[32] + * + * "size" is the size of the allocation, "backtrace" is a fixed-size + * array of function pointers, and "allocations" is the number of + * allocations with the exact same size and backtrace. + * + * The entries are sorted by descending total size (i.e. size*allocations) + * then allocation count. For best results with "diff" we'd like to sort + * primarily by individual size then stack trace. Since the entries are + * fixed-size, and we're allowed (by the current implementation) to mangle + * them, we can do this in place. + */ +static void dumpNativeHeap(FILE* fp) +{ + uint8_t* info = NULL; + size_t overallSize, infoSize, totalMemory, backtraceSize; + + get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, + &backtraceSize); + if (info == NULL) { + fprintf(fp, "Native heap dump not available. To enable, run these" + " commands (requires root):\n"); + fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n"); + fprintf(fp, "$ adb shell stop\n"); + fprintf(fp, "$ adb shell start\n"); + return; + } + assert(infoSize != 0); + assert(overallSize % infoSize == 0); + + fprintf(fp, "Android Native Heap Dump v1.0\n\n"); + + size_t recordCount = overallSize / infoSize; + fprintf(fp, "Total memory: %zu\n", totalMemory); + fprintf(fp, "Allocation records: %zd\n", recordCount); + if (backtraceSize != BACKTRACE_SIZE) { + fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n", + backtraceSize, BACKTRACE_SIZE); + } + fprintf(fp, "\n"); + + /* re-sort the entries */ + qsort(info, recordCount, infoSize, compareHeapRecords); + + /* dump the entries to the file */ + const uint8_t* ptr = info; + for (size_t idx = 0; idx < recordCount; idx++) { + size_t size = *(size_t*) ptr; + size_t allocations = *(size_t*) (ptr + sizeof(size_t)); + intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2); + + fprintf(fp, "z %d sz %8zu num %4zu bt", + (size & SIZE_FLAG_ZYGOTE_CHILD) != 0, + size & ~SIZE_FLAG_ZYGOTE_CHILD, + allocations); + for (size_t bt = 0; bt < backtraceSize; bt++) { + if (backtrace[bt] == 0) { + break; + } else { + fprintf(fp, " %08x", backtrace[bt]); + } + } + fprintf(fp, "\n"); + + ptr += infoSize; + } + + fprintf(fp, "END\n"); + free_malloc_leak_info(info); +} +#endif /*HAVE_ANDROID_OS*/ + +/* + * Dump the native heap, writing human-readable output to the specified + * file descriptor. + */ +static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, + jobject fileDescriptor) +{ + if (fileDescriptor == NULL) { + jniThrowNullPointerException(env, NULL); + return; + } + int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (origFd < 0) { + jniThrowRuntimeException(env, "Invalid file descriptor"); + return; + } + + /* dup() the descriptor so we don't close the original with fclose() */ + int fd = dup(origFd); + if (fd < 0) { + LOGW("dup(%d) failed: %s\n", origFd, strerror(errno)); + jniThrowRuntimeException(env, "dup() failed"); + return; + } + + FILE* fp = fdopen(fd, "w"); + if (fp == NULL) { + LOGW("fdopen(%d) failed: %s\n", fd, strerror(errno)); + close(fd); + jniThrowRuntimeException(env, "fdopen() failed"); + return; + } + +#ifdef HAVE_ANDROID_OS + LOGD("Native heap dump starting...\n"); + dumpNativeHeap(fp); + LOGD("Native heap dump complete.\n"); +#else + fprintf(fp, "Native heap dump not available on this platform\n"); +#endif + + fclose(fp); +} + + /* * JNI registration. */ @@ -289,6 +462,8 @@ static JNINativeMethod gMethods[] = { (void*) android_os_Debug_getDirtyPages }, { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V", (void*) android_os_Debug_getDirtyPagesPid }, + { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", + (void*) android_os_Debug_dumpNativeHeap }, { "getBinderSentTransactions", "()I", (void*) android_os_Debug_getBinderSentTransactions }, { "getBinderReceivedTransactions", "()I", @@ -320,4 +495,4 @@ int register_android_os_Debug(JNIEnv *env) return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods)); } -}; +}; // namespace android diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp index 21cb919..d3faa2f 100644 --- a/core/jni/android_os_FileUtils.cpp +++ b/core/jni/android_os_FileUtils.cpp @@ -113,6 +113,11 @@ jint android_os_FileUtils_getPermissions(JNIEnv* env, jobject clazz, #endif } +jint android_os_FileUtils_setUMask(JNIEnv* env, jobject clazz, jint mask) +{ + return umask(mask); +} + jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path) { #if HAVE_ANDROID_OS @@ -170,6 +175,7 @@ jboolean android_os_FileUtils_getFileStatus(JNIEnv* env, jobject clazz, jstring 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}, + {"setUMask", "(I)I", (void*)android_os_FileUtils_setUMask}, {"getFatVolumeId", "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId}, {"getFileStatus", "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z", (void*)android_os_FileUtils_getFileStatus}, }; diff --git a/core/jni/android_os_ParcelFileDescriptor.cpp b/core/jni/android_os_ParcelFileDescriptor.cpp index 848a57a..eceef1c 100644 --- a/core/jni/android_os_ParcelFileDescriptor.cpp +++ b/core/jni/android_os_ParcelFileDescriptor.cpp @@ -66,6 +66,26 @@ static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromSocket(JNIEn return fileDescriptorClone; } +static int android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env, + jobject clazz, jobjectArray outFds) +{ + int fds[2]; + if (pipe(fds) < 0) { + return -errno; + } + + for (int i=0; i<2; i++) { + jobject fdObj = env->NewObject(gFileDescriptorOffsets.mClass, + gFileDescriptorOffsets.mConstructor); + if (fdObj != NULL) { + env->SetIntField(fdObj, gFileDescriptorOffsets.mDescriptor, fds[i]); + } + env->SetObjectArrayElement(outFds, i, fdObj); + } + + return 0; +} + static jint getFd(JNIEnv* env, jobject clazz) { jobject descriptor = env->GetObjectField(clazz, gParcelFileDescriptorOffsets.mFileDescriptor); @@ -109,6 +129,8 @@ static jlong android_os_ParcelFileDescriptor_seekTo(JNIEnv* env, static const JNINativeMethod gParcelFileDescriptorMethods[] = { {"getFileDescriptorFromSocket", "(Ljava/net/Socket;)Ljava/io/FileDescriptor;", (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromSocket}, + {"createPipeNative", "([Ljava/io/FileDescriptor;)I", + (void*)android_os_ParcelFileDescriptor_createPipeNative}, {"getStatSize", "()J", (void*)android_os_ParcelFileDescriptor_getStatSize}, {"seekTo", "(J)J", diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 01b6711..1307ec3 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -50,6 +50,8 @@ static jmethodID method_onDeviceDisappeared; static jmethodID method_onDeviceCreated; static jmethodID method_onDeviceRemoved; static jmethodID method_onDeviceDisconnectRequested; +static jmethodID method_onNetworkDeviceDisconnected; +static jmethodID method_onNetworkDeviceConnected; static jmethodID method_onCreatePairedDeviceResult; static jmethodID method_onCreateDeviceResult; @@ -64,6 +66,11 @@ static jmethodID method_onDisplayPasskey; static jmethodID method_onAgentAuthorize; static jmethodID method_onAgentCancel; +static jmethodID method_onInputDevicePropertyChanged; +static jmethodID method_onInputDeviceConnectionResult; +static jmethodID method_onPanDevicePropertyChanged; +static jmethodID method_onPanDeviceConnectionResult; + typedef event_loop_native_data_t native_data_t; #define EVENT_LOOP_REFS 10 @@ -95,6 +102,10 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V"); method_onDeviceDisconnectRequested = env->GetMethodID(clazz, "onDeviceDisconnectRequested", "(Ljava/lang/String;)V"); + method_onNetworkDeviceConnected = env->GetMethodID(clazz, "onNetworkDeviceConnected", + "(Ljava/lang/String;I)V"); + method_onNetworkDeviceDisconnected = env->GetMethodID(clazz, "onNetworkDeviceDisconnected", + "(Ljava/lang/String;)V"); method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult", "(Ljava/lang/String;I)V"); @@ -116,6 +127,14 @@ static void classInitNative(JNIEnv* env, jclass clazz) { "(Ljava/lang/String;I)V"); method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey", "(Ljava/lang/String;II)V"); + method_onInputDevicePropertyChanged = env->GetMethodID(clazz, "onInputDevicePropertyChanged", + "(Ljava/lang/String;[Ljava/lang/String;)V"); + method_onInputDeviceConnectionResult = env->GetMethodID(clazz, "onInputDeviceConnectionResult", + "(Ljava/lang/String;Z)V"); + method_onPanDevicePropertyChanged = env->GetMethodID(clazz, "onPanDevicePropertyChanged", + "(Ljava/lang/String;[Ljava/lang/String;)V"); + method_onPanDeviceConnectionResult = env->GetMethodID(clazz, "onPanDeviceConnectionResult", + "(Ljava/lang/String;Z)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); #endif @@ -226,6 +245,27 @@ static jboolean setUpEventLoop(native_data_t *nat) { return JNI_FALSE; } dbus_bus_add_match(nat->conn, + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Input'", + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + return JNI_FALSE; + } + dbus_bus_add_match(nat->conn, + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Network'", + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + return JNI_FALSE; + } + dbus_bus_add_match(nat->conn, + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".NetworkServer'", + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + return JNI_FALSE; + } + dbus_bus_add_match(nat->conn, "type='signal',interface='org.bluez.AudioSink'", &err); if (dbus_error_is_set(&err)) { @@ -382,13 +422,31 @@ static void tearDownEventLoop(native_data_t *nat) { dbus_connection_unregister_object_path(nat->conn, agent_path); dbus_bus_remove_match(nat->conn, - "type='signal',interface='org.bluez.AudioSink'", + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".AudioSink'", + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } + dbus_bus_remove_match(nat->conn, + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); } dbus_bus_remove_match(nat->conn, - "type='signal',interface='org.bluez.audio.Device'", + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Input'", + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } + dbus_bus_remove_match(nat->conn, + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Network'", + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } + dbus_bus_remove_match(nat->conn, + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".NetworkServer'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); @@ -853,6 +911,70 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, method_onDeviceDisconnectRequested, env->NewStringUTF(remote_device_path)); goto success; + } else if (dbus_message_is_signal(msg, + "org.bluez.Input", + "PropertyChanged")) { + + jobjectArray str_array = + parse_input_property_change(env, msg); + if (str_array != NULL) { + const char *c_path = dbus_message_get_path(msg); + env->CallVoidMethod(nat->me, + method_onInputDevicePropertyChanged, + env->NewStringUTF(c_path), + str_array); + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + } + goto success; + } else if (dbus_message_is_signal(msg, + "org.bluez.Network", + "PropertyChanged")) { + + jobjectArray str_array = + parse_pan_property_change(env, msg); + if (str_array != NULL) { + const char *c_path = dbus_message_get_path(msg); + env->CallVoidMethod(nat->me, + method_onPanDevicePropertyChanged, + env->NewStringUTF(c_path), + str_array); + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + } + goto success; + } else if (dbus_message_is_signal(msg, + "org.bluez.NetworkServer", + "DeviceDisconnected")) { + char *c_address; + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID)) { + env->CallVoidMethod(nat->me, + method_onNetworkDeviceDisconnected, + env->NewStringUTF(c_address)); + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + } + goto success; + } else if (dbus_message_is_signal(msg, + "org.bluez.NetworkServer", + "DeviceConnected")) { + char *c_address; + uint16_t uuid; + + if (dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_UINT16, &uuid, + DBUS_TYPE_INVALID)) { + env->CallVoidMethod(nat->me, + method_onNetworkDeviceConnected, + env->NewStringUTF(c_address), + uuid); + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + } + goto success; } ret = a2dp_event_filter(msg, env); @@ -1210,6 +1332,57 @@ done: env->DeleteLocalRef(addr); free(user); } + +void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *n) { + LOGV(__FUNCTION__); + + native_data_t *nat = (native_data_t *)n; + const char *path = (const char *)user; + DBusError err; + dbus_error_init(&err); + JNIEnv *env; + nat->vm->GetEnv((void**)&env, nat->envVer); + + bool result = JNI_TRUE; + if (dbus_set_error_from_message(&err, msg)) { + LOG_AND_FREE_DBUS_ERROR(&err); + result = JNI_FALSE; + } + LOGV("... Device Path = %s, result = %d", path, result); + jstring jPath = env->NewStringUTF(path); + env->CallVoidMethod(nat->me, + method_onInputDeviceConnectionResult, + jPath, + result); + env->DeleteLocalRef(jPath); + free(user); +} + +void onPanDeviceConnectionResult(DBusMessage *msg, void *user, void *n) { + LOGV(__FUNCTION__); + + native_data_t *nat = (native_data_t *)n; + const char *path = (const char *)user; + DBusError err; + dbus_error_init(&err); + JNIEnv *env; + nat->vm->GetEnv((void**)&env, nat->envVer); + + bool result = JNI_TRUE; + if (dbus_set_error_from_message(&err, msg)) { + LOG_AND_FREE_DBUS_ERROR(&err); + result = JNI_FALSE; + } + LOGV("... Pan Device Path = %s, result = %d", path, result); + jstring jPath = env->NewStringUTF(path); + env->CallVoidMethod(nat->me, + method_onPanDeviceConnectionResult, + jPath, + result); + env->DeleteLocalRef(jPath); + free(user); +} + #endif static JNINativeMethod sMethods[] = { diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index 4420aca..74127cf 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -16,6 +16,11 @@ #define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter" #define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device" +#define DBUS_INPUT_IFACE BLUEZ_DBUS_BASE_IFC ".Input" +#define DBUS_NETWORK_IFACE BLUEZ_DBUS_BASE_IFC ".Network" +#define DBUS_NETWORKSERVER_IFACE BLUEZ_DBUS_BASE_IFC ".NetworkServer" + + #define LOG_TAG "BluetoothService.cpp" #include "android_bluetooth_common.h" @@ -68,6 +73,9 @@ extern DBusHandlerResult agent_event_filter(DBusConnection *conn, void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat); void onDiscoverServicesResult(DBusMessage *msg, void *user, void *nat); void onCreateDeviceResult(DBusMessage *msg, void *user, void *nat); +void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *nat); +void onConnectPanResult(DBusMessage *msg, void *user, void *n); +void onPanDeviceConnectionResult(DBusMessage *msg, void *user, void *nat); /** Get native data stored in the opaque (Java code maintained) pointer mNativeData @@ -881,6 +889,160 @@ static jboolean setLinkTimeoutNative(JNIEnv *env, jobject object, jstring object return JNI_FALSE; } +static jboolean connectInputDeviceNative(JNIEnv *env, jobject object, jstring path) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { + const char *c_path = env->GetStringUTFChars(path, NULL); + + int len = env->GetStringLength(path) + 1; + char *context_path = (char *)calloc(len, sizeof(char)); + strlcpy(context_path, c_path, len); // for callback + + bool ret = dbus_func_args_async(env, nat->conn, -1, onInputDeviceConnectionResult, + context_path, eventLoopNat, c_path, DBUS_INPUT_IFACE, + "Connect", + DBUS_TYPE_INVALID); + + env->ReleaseStringUTFChars(path, c_path); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jboolean disconnectInputDeviceNative(JNIEnv *env, jobject object, + jstring path) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { + const char *c_path = env->GetStringUTFChars(path, NULL); + + int len = env->GetStringLength(path) + 1; + char *context_path = (char *)calloc(len, sizeof(char)); + strlcpy(context_path, c_path, len); // for callback + + bool ret = dbus_func_args_async(env, nat->conn, -1, onInputDeviceConnectionResult, + context_path, eventLoopNat, c_path, DBUS_INPUT_IFACE, + "Disconnect", + DBUS_TYPE_INVALID); + + env->ReleaseStringUTFChars(path, c_path); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jboolean setBluetoothTetheringNative(JNIEnv *env, jobject object, jboolean value, + jstring src_role, jstring bridge) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply; + const char *c_role = env->GetStringUTFChars(src_role, NULL); + const char *c_bridge = env->GetStringUTFChars(bridge, NULL); + if (value) { + LOGE("setBluetoothTetheringNative true"); + reply = dbus_func_args(env, nat->conn, + get_adapter_path(env, object), + DBUS_NETWORKSERVER_IFACE, + "Register", + DBUS_TYPE_STRING, &c_role, + DBUS_TYPE_STRING, &c_bridge, + DBUS_TYPE_INVALID); + } else { + LOGE("setBluetoothTetheringNative false"); + reply = dbus_func_args(env, nat->conn, + get_adapter_path(env, object), + DBUS_NETWORKSERVER_IFACE, + "Unregister", + DBUS_TYPE_STRING, &c_role, + DBUS_TYPE_INVALID); + } + env->ReleaseStringUTFChars(src_role, c_role); + env->ReleaseStringUTFChars(bridge, c_bridge); + return reply ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jboolean connectPanDeviceNative(JNIEnv *env, jobject object, jstring path, + jstring srcRole, jstring dstRole) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + LOGE("connectPanDeviceNative"); + native_data_t *nat = get_native_data(env, object); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { + const char *c_path = env->GetStringUTFChars(path, NULL); + const char *src = env->GetStringUTFChars(srcRole, NULL); + const char *dst = env->GetStringUTFChars(dstRole, NULL); + + int len = env->GetStringLength(path) + 1; + char *context_path = (char *)calloc(len, sizeof(char)); + strlcpy(context_path, c_path, len); // for callback + + bool ret = dbus_func_args_async(env, nat->conn, -1,onPanDeviceConnectionResult, + context_path, eventLoopNat, c_path, + DBUS_NETWORK_IFACE, "Connect", + DBUS_TYPE_STRING, &src, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_INVALID); + + env->ReleaseStringUTFChars(path, c_path); + env->ReleaseStringUTFChars(srcRole, src); + env->ReleaseStringUTFChars(dstRole, dst); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jboolean disconnectPanDeviceNative(JNIEnv *env, jobject object, + jstring path) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + LOGE("disconnectPanDeviceNative"); + native_data_t *nat = get_native_data(env, object); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { + const char *c_path = env->GetStringUTFChars(path, NULL); + + int len = env->GetStringLength(path) + 1; + char *context_path = (char *)calloc(len, sizeof(char)); + strlcpy(context_path, c_path, len); // for callback + + bool ret = dbus_func_args_async(env, nat->conn, -1,onPanDeviceConnectionResult, + context_path, eventLoopNat, c_path, + DBUS_NETWORK_IFACE, "Disconnect", + DBUS_TYPE_INVALID); + + env->ReleaseStringUTFChars(path, c_path); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"classInitNative", "()V", (void*)classInitNative}, @@ -926,8 +1088,18 @@ static JNINativeMethod sMethods[] = { {"addRfcommServiceRecordNative", "(Ljava/lang/String;JJS)I", (void *)addRfcommServiceRecordNative}, {"removeServiceRecordNative", "(I)Z", (void *)removeServiceRecordNative}, {"setLinkTimeoutNative", "(Ljava/lang/String;I)Z", (void *)setLinkTimeoutNative}, + // HID functions + {"connectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)connectInputDeviceNative}, + {"disconnectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectInputDeviceNative}, + + {"setBluetoothTetheringNative", "(ZLjava/lang/String;Ljava/lang/String;)Z", + (void *)setBluetoothTetheringNative}, + {"connectPanDeviceNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z", + (void *)connectPanDeviceNative}, + {"disconnectPanDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectPanDeviceNative}, }; + int register_android_server_BluetoothService(JNIEnv *env) { return AndroidRuntime::registerNativeMethods(env, "android/server/BluetoothService", sMethods, NELEM(sMethods)); diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp new file mode 100644 index 0000000..aa71746 --- /dev/null +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2010 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 "OpenGLRenderer" + +#include "jni.h" +#include <nativehelper/JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> +#include <utils/ResourceTypes.h> + +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkMatrix.h> +#include <SkPaint.h> +#include <SkRegion.h> +#include <SkScalerContext.h> +#include <SkTemplates.h> +#include <SkXfermode.h> + +#include <OpenGLRenderer.h> +#include <SkiaShader.h> +#include <SkiaColorFilter.h> +#include <Rect.h> + +#include "TextLayout.h" + +namespace android { + +using namespace uirenderer; + +/** + * Note: OpenGLRenderer JNI layer is generated and compiled only on supported + * devices. This means all the logic must be compiled only when the + * preprocessor variable USE_OPENGL_RENDERER is defined. + */ +#ifdef USE_OPENGL_RENDERER + +// ---------------------------------------------------------------------------- +// Java APIs +// ---------------------------------------------------------------------------- + +static struct { + jclass clazz; + jmethodID set; +} gRectClassInfo; + +// ---------------------------------------------------------------------------- +// Constructors +// ---------------------------------------------------------------------------- + +static OpenGLRenderer* android_view_GLES20Canvas_createRenderer(JNIEnv* env, jobject canvas) { + return new OpenGLRenderer; +} + +static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer) { + delete renderer; +} + +// ---------------------------------------------------------------------------- +// Setup +// ---------------------------------------------------------------------------- + +static void android_view_GLES20Canvas_setViewport(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jint width, jint height) { + renderer->setViewport(width, height); +} + +static void android_view_GLES20Canvas_prepare(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer) { + renderer->prepare(); +} + +static void android_view_GLES20Canvas_acquireContext(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer) { + renderer->acquireContext(); +} + +static void android_view_GLES20Canvas_releaseContext(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer) { + renderer->releaseContext(); +} + +// ---------------------------------------------------------------------------- +// State +// ---------------------------------------------------------------------------- + +static jint android_view_GLES20Canvas_save(JNIEnv* env, jobject canvas, OpenGLRenderer* renderer, + jint flags) { + return renderer->save(flags); +} + +static jint android_view_GLES20Canvas_getSaveCount(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer) { + return renderer->getSaveCount(); +} + +static void android_view_GLES20Canvas_restore(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer) { + renderer->restore(); +} + +static void android_view_GLES20Canvas_restoreToCount(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jint saveCount) { + renderer->restoreToCount(saveCount); +} + +// ---------------------------------------------------------------------------- +// Layers +// ---------------------------------------------------------------------------- + +static jint android_view_GLES20Canvas_saveLayer(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom, + SkPaint* paint, jint saveFlags) { + return renderer->saveLayer(left, top, right, bottom, paint, saveFlags); +} + +static jint android_view_GLES20Canvas_saveLayerAlpha(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom, + jint alpha, jint saveFlags) { + return renderer->saveLayerAlpha(left, top, right, bottom, alpha, saveFlags); +} + +// ---------------------------------------------------------------------------- +// Clipping +// ---------------------------------------------------------------------------- + +static bool android_view_GLES20Canvas_quickReject(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom, + SkCanvas::EdgeType edge) { + return renderer->quickReject(left, top, right, bottom); +} + +static bool android_view_GLES20Canvas_clipRectF(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom, + SkRegion::Op op) { + return renderer->clipRect(left, top, right, bottom, op); +} + +static bool android_view_GLES20Canvas_clipRect(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jint left, jint top, jint right, jint bottom, + SkRegion::Op op) { + return renderer->clipRect(float(left), float(top), float(right), float(bottom), op); +} + +static bool android_view_GLES20Canvas_getClipBounds(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jobject rect) { + const android::uirenderer::Rect& bounds(renderer->getClipBounds()); + + env->CallVoidMethod(rect, gRectClassInfo.set, + int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom)); + + return !bounds.isEmpty(); +} + +// ---------------------------------------------------------------------------- +// Transforms +// ---------------------------------------------------------------------------- + +static void android_view_GLES20Canvas_translate(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat dx, jfloat dy) { + renderer->translate(dx, dy); +} + +static void android_view_GLES20Canvas_rotate(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat degrees) { + renderer->rotate(degrees); +} + +static void android_view_GLES20Canvas_scale(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat sx, jfloat sy) { + renderer->scale(sx, sy); +} + +static void android_view_GLES20Canvas_setMatrix(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkMatrix* matrix) { + renderer->setMatrix(matrix); +} + +static void android_view_GLES20Canvas_getMatrix(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkMatrix* matrix) { + renderer->getMatrix(matrix); +} + +static void android_view_GLES20Canvas_concatMatrix(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkMatrix* matrix) { + renderer->concatMatrix(matrix); +} + +// ---------------------------------------------------------------------------- +// Drawing +// ---------------------------------------------------------------------------- + +static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkBitmap* bitmap, float left, float top, SkPaint* paint) { + renderer->drawBitmap(bitmap, left, top, paint); +} + +static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkBitmap* bitmap, + float srcLeft, float srcTop, float srcRight, float srcBottom, + float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint) { + renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom, + dstLeft, dstTop, dstRight, dstBottom, paint); +} + +static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) { + renderer->drawBitmap(bitmap, matrix, paint); +} + +static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray chunks, + float left, float top, float right, float bottom, SkPaint* paint) { + jbyte* storage = env->GetByteArrayElements(chunks, NULL); + Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(storage); + Res_png_9patch::deserialize(patch); + + renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint); + + env->ReleaseByteArrayElements(chunks, storage, 0); +} + +static void android_view_GLES20Canvas_drawColor(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jint color, SkXfermode::Mode mode) { + renderer->drawColor(color, mode); +} + +static void android_view_GLES20Canvas_drawRect(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom, + SkPaint* paint) { + renderer->drawRect(left, top, right, bottom, paint); +} + +static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkRegion* region, SkPaint* paint) { + SkRegion::Iterator it(*region); + while (!it.done()) { + const SkIRect& r = it.rect(); + renderer->drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint); + it.next(); + } +} + +static void android_view_GLES20Canvas_drawPath(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkPath* path, SkPaint* paint) { + renderer->drawPath(path, paint); +} + +// ---------------------------------------------------------------------------- +// Shaders and color filters +// ---------------------------------------------------------------------------- + +static void android_view_GLES20Canvas_resetModifiers(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer) { + renderer->resetShader(); + renderer->resetColorFilter(); + renderer->resetShadow(); +} + +static void android_view_GLES20Canvas_setupShader(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkiaShader* shader) { + renderer->setupShader(shader); +} + +static void android_view_GLES20Canvas_setupColorFilter(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkiaColorFilter* filter) { + renderer->setupColorFilter(filter); +} + +static void android_view_GLES20Canvas_setupShadow(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat radius, jfloat dx, jfloat dy, jint color) { + renderer->setupShadow(radius, dx, dy, color); +} + +// ---------------------------------------------------------------------------- +// Text +// ---------------------------------------------------------------------------- + +static void renderText(OpenGLRenderer* renderer, const jchar* text, int count, + jfloat x, jfloat y, int flags, SkPaint* paint) { + const jchar *workText; + jchar* buffer = NULL; + int32_t workBytes; + if (TextLayout::prepareText(paint, text, count, flags, &workText, &workBytes, &buffer)) { + renderer->drawText((const char*) workText, workBytes, count, x, y, paint); + free(buffer); + } +} + +static void renderTextRun(OpenGLRenderer* renderer, const jchar* text, + jint start, jint count, jint contextCount, jfloat x, jfloat y, + int flags, SkPaint* paint) { + uint8_t rtl = flags & 0x1; + if (rtl) { + SkAutoSTMalloc<80, jchar> buffer(contextCount); + jchar* shaped = buffer.get(); + if (TextLayout::prepareRtlTextRun(text, start, count, contextCount, shaped)) { + renderer->drawText((const char*) shaped, count << 1, count, x, y, paint); + } else { + LOGW("drawTextRun error"); + } + } else { + renderer->drawText((const char*) (text + start), count << 1, count, x, y, paint); + } +} + +static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jcharArray text, int index, int count, + jfloat x, jfloat y, int flags, SkPaint* paint) { + jchar* textArray = env->GetCharArrayElements(text, NULL); + renderText(renderer, textArray + index, count, x, y, flags, paint); + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); +} + +static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jstring text, int start, int end, + jfloat x, jfloat y, int flags, SkPaint* paint) { + const jchar* textArray = env->GetStringChars(text, NULL); + renderText(renderer, textArray + start, end - start, x, y, flags, paint); + env->ReleaseStringChars(text, textArray); +} + +static void android_view_GLES20Canvas_drawTextRunArray(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jcharArray text, int index, int count, + int contextIndex, int contextCount, jfloat x, jfloat y, int dirFlags, + SkPaint* paint) { + jchar* textArray = env->GetCharArrayElements(text, NULL); + renderTextRun(renderer, textArray + contextIndex, index - contextIndex, + count, contextCount, x, y, dirFlags, paint); + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); + } + +static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jstring text, int start, int end, + int contextStart, int contextEnd, jfloat x, jfloat y, int dirFlags, + SkPaint* paint) { + const jchar* textArray = env->GetStringChars(text, NULL); + jint count = end - start; + jint contextCount = contextEnd - contextStart; + renderTextRun(renderer, textArray + contextStart, start - contextStart, + count, contextCount, x, y, dirFlags, paint); + env->ReleaseStringChars(text, textArray); +} + +#endif // USE_OPENGL_RENDERER + +// ---------------------------------------------------------------------------- +// Common +// ---------------------------------------------------------------------------- + +static jboolean android_view_GLES20Canvas_isAvailable(JNIEnv* env, jobject clazz) { +#ifdef USE_OPENGL_RENDERER + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/view/GLES20Canvas"; + +static JNINativeMethod gMethods[] = { + { "nIsAvailable", "()Z", (void*) android_view_GLES20Canvas_isAvailable }, + +#ifdef USE_OPENGL_RENDERER + { "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer }, + { "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer }, + { "nSetViewport", "(III)V", (void*) android_view_GLES20Canvas_setViewport }, + { "nPrepare", "(I)V", (void*) android_view_GLES20Canvas_prepare }, + { "nAcquireContext", "(I)V", (void*) android_view_GLES20Canvas_acquireContext }, + { "nReleaseContext", "(I)V", (void*) android_view_GLES20Canvas_releaseContext }, + + { "nSave", "(II)I", (void*) android_view_GLES20Canvas_save }, + { "nRestore", "(I)V", (void*) android_view_GLES20Canvas_restore }, + { "nRestoreToCount", "(II)V", (void*) android_view_GLES20Canvas_restoreToCount }, + { "nGetSaveCount", "(I)I", (void*) android_view_GLES20Canvas_getSaveCount }, + + { "nSaveLayer", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayer }, + { "nSaveLayerAlpha", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayerAlpha }, + + { "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject }, + { "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF }, + { "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect }, + + { "nTranslate", "(IFF)V", (void*) android_view_GLES20Canvas_translate }, + { "nRotate", "(IF)V", (void*) android_view_GLES20Canvas_rotate }, + { "nScale", "(IFF)V", (void*) android_view_GLES20Canvas_scale }, + + { "nSetMatrix", "(II)V", (void*) android_view_GLES20Canvas_setMatrix }, + { "nGetMatrix", "(II)V", (void*) android_view_GLES20Canvas_getMatrix }, + { "nConcatMatrix", "(II)V", (void*) android_view_GLES20Canvas_concatMatrix }, + + { "nDrawBitmap", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawBitmap }, + { "nDrawBitmap", "(IIFFFFFFFFI)V", (void*) android_view_GLES20Canvas_drawBitmapRect }, + { "nDrawBitmap", "(IIII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix }, + { "nDrawPatch", "(II[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch }, + { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor }, + { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect }, + { "nDrawRects", "(III)V", (void*) android_view_GLES20Canvas_drawRects }, + { "nDrawPath", "(III)V", (void*) android_view_GLES20Canvas_drawPath }, + + { "nResetModifiers", "(I)V", (void*) android_view_GLES20Canvas_resetModifiers }, + { "nSetupShader", "(II)V", (void*) android_view_GLES20Canvas_setupShader }, + { "nSetupColorFilter", "(II)V", (void*) android_view_GLES20Canvas_setupColorFilter }, + { "nSetupShadow", "(IFFFI)V", (void*) android_view_GLES20Canvas_setupShadow }, + + { "nDrawText", "(I[CIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArray }, + { "nDrawText", "(ILjava/lang/String;IIFFII)V", + (void*) android_view_GLES20Canvas_drawText }, + + { "nDrawTextRun", "(I[CIIIIFFII)V", (void*) android_view_GLES20Canvas_drawTextRunArray }, + { "nDrawTextRun", "(ILjava/lang/String;IIIIFFII)V", + (void*) android_view_GLES20Canvas_drawTextRun }, + + { "nGetClipBounds", "(ILandroid/graphics/Rect;)Z", + (void*) android_view_GLES20Canvas_getClipBounds }, +#endif +}; + +#ifdef USE_OPENGL_RENDERER + #define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + + #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method " methodName); +#else + #define FIND_CLASS(var, className) + #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) +#endif + +int register_android_view_GLES20Canvas(JNIEnv* env) { + FIND_CLASS(gRectClassInfo.clazz, "android/graphics/Rect"); + GET_METHOD_ID(gRectClassInfo.set, gRectClassInfo.clazz, "set", "(IIII)V"); + + return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +}; diff --git a/core/jni/android_view_ViewRoot.cpp b/core/jni/android_view_ViewRoot.cpp index 5173bb8..2988ae8 100644 --- a/core/jni/android_view_ViewRoot.cpp +++ b/core/jni/android_view_ViewRoot.cpp @@ -76,10 +76,6 @@ static void android_view_ViewRoot_showFPS(JNIEnv* env, jobject, jobject jcanvas, canvas->restore(); } -static void android_view_ViewRoot_abandonGlCaches(JNIEnv* env, jobject) { - SkGLCanvas::AbandonAllTextures(); -} - // ---------------------------------------------------------------------------- @@ -87,9 +83,7 @@ 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 } + (void*)android_view_ViewRoot_showFPS } }; int register_android_view_ViewRoot(JNIEnv* env) { |